update src

This commit is contained in:
QIDI TECH
2025-05-05 15:13:42 +08:00
parent 3fe258372a
commit eae8e18c3a
35 changed files with 11268 additions and 6351 deletions

View File

@@ -504,7 +504,7 @@ find_package(cereal REQUIRED)
set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/i18n") set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/i18n")
set(QDT_L18N_DIR "${CMAKE_CURRENT_SOURCE_DIR}/qdt/i18n") set(QDT_L18N_DIR "${CMAKE_CURRENT_SOURCE_DIR}/qdt/i18n")
add_custom_target(gettext_make_pot add_custom_target(gettext_make_pot
COMMAND xgettext --keyword=L --keyword=_L --keyword=_u8L --keyword=L_CONTEXT:1,2c --keyword=_L_PLURAL:1,2 --add-comments=TRN --from-code=UTF-8 --no-location --debug --boost COMMAND xgettext --keyword=L --no-wrap --keyword=_L --keyword=_u8L --keyword=L_CONTEXT:1,2c --keyword=_L_PLURAL:1,2 --add-comments=TRN --from-code=UTF-8 --no-location --debug --boost
-f "${QDT_L18N_DIR}/list.txt" -f "${QDT_L18N_DIR}/list.txt"
-o "${QDT_L18N_DIR}/QIDIStudio.pot" -o "${QDT_L18N_DIR}/QIDIStudio.pot"
COMMAND hintsToPot ${SLIC3R_RESOURCES_DIR} ${QDT_L18N_DIR} COMMAND hintsToPot ${SLIC3R_RESOURCES_DIR} ${QDT_L18N_DIR}
@@ -521,7 +521,7 @@ foreach(po_file ${QDT_L10N_PO_FILES})
SET(po_new_file "${po_dir}/QIDIStudio_.po") SET(po_new_file "${po_dir}/QIDIStudio_.po")
add_custom_command( add_custom_command(
TARGET gettext_merge_po_with_pot PRE_BUILD TARGET gettext_merge_po_with_pot PRE_BUILD
COMMAND msgmerge -N -o ${po_file} ${po_file} "${QDT_L18N_DIR}/QIDIStudio.pot" COMMAND msgmerge --no-wrap -N -o ${po_file} ${po_file} "${QDT_L18N_DIR}/QIDIStudio.pot"
DEPENDS ${po_file} DEPENDS ${po_file}
) )
endforeach() endforeach()

View File

@@ -25,6 +25,7 @@ add_subdirectory(libslic3r)
if (SLIC3R_GUI) if (SLIC3R_GUI)
add_subdirectory(imgui) add_subdirectory(imgui)
add_subdirectory(imguizmo)
add_subdirectory(hidapi) add_subdirectory(hidapi)
include_directories(hidapi/include) include_directories(hidapi/include)

File diff suppressed because it is too large Load Diff

View File

@@ -6,9 +6,6 @@
#include <Windows.h> #include <Windows.h>
#include <shellapi.h> #include <shellapi.h>
#include <wchar.h> #include <wchar.h>
#ifdef SLIC3R_GUI #ifdef SLIC3R_GUI
extern "C" extern "C"
{ {
@@ -18,22 +15,16 @@ extern "C"
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 0; __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 0;
} }
#endif /* SLIC3R_GUI */ #endif /* SLIC3R_GUI */
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#ifdef SLIC3R_GUI #ifdef SLIC3R_GUI
#include <GL/GL.h> #include <GL/GL.h>
#endif /* SLIC3R_GUI */ #endif /* SLIC3R_GUI */
#include <string> #include <string>
#include <vector> #include <vector>
#include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/classification.hpp>
#include <stdio.h> #include <stdio.h>
#ifdef SLIC3R_GUI #ifdef SLIC3R_GUI
class OpenGLVersionCheck class OpenGLVersionCheck
{ {
@@ -42,42 +33,45 @@ public:
std::string glsl_version; std::string glsl_version;
std::string vendor; std::string vendor;
std::string renderer; std::string renderer;
HINSTANCE hOpenGL = nullptr; HINSTANCE hOpenGL = nullptr;
bool success = false; bool success = false;
bool load_opengl_dll() bool load_opengl_dll()
{ {
MSG msg = {0}; MSG msg = { 0 };
WNDCLASS wc = {0}; WNDCLASS wc = { 0 };
wc.lpfnWndProc = OpenGLVersionCheck::supports_opengl2_wndproc; wc.lpfnWndProc = OpenGLVersionCheck::supports_opengl2_wndproc;
wc.hInstance = (HINSTANCE)GetModuleHandle(nullptr); wc.hInstance = (HINSTANCE)GetModuleHandle(nullptr);
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND); wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
wc.lpszClassName = L"QIDIStudio_opengl_version_check"; wc.lpszClassName = L"BambuStudio_opengl_version_check";
wc.style = CS_OWNDC; wc.style = CS_OWNDC;
if (RegisterClass(&wc)) { if (RegisterClass(&wc)) {
HWND hwnd = CreateWindowW(wc.lpszClassName, L"QIDIStudio_opengl_version_check", WS_OVERLAPPEDWINDOW, 0, 0, 640, 480, 0, 0, wc.hInstance, (LPVOID)this); HWND hwnd = CreateWindowW(wc.lpszClassName, L"BambuStudio_opengl_version_check", WS_OVERLAPPEDWINDOW, 0, 0, 640, 480, 0, 0, wc.hInstance, (LPVOID)this);
if (hwnd) { if (hwnd) {
message_pump_exit = false; message_pump_exit = false;
while (GetMessage(&msg, NULL, 0, 0 ) > 0 && ! message_pump_exit) while (GetMessage(&msg, NULL, 0, 0) > 0 && !message_pump_exit)
DispatchMessage(&msg); DispatchMessage(&msg);
} }
} }
return this->success; return this->success;
} }
bool unload_opengl_dll()
void unload_opengl_dll()
{ {
if (this->hOpenGL) { if (this->hOpenGL != nullptr) {
BOOL released = FreeLibrary(this->hOpenGL); if (::FreeLibrary(this->hOpenGL) != FALSE) {
if (released) if (::GetModuleHandle(L"opengl32.dll") == nullptr) {
printf("System OpenGL library released\n"); printf("System OpenGL library successfully released\n");
this->hOpenGL = nullptr;
return true;
}
else
printf("System OpenGL library released but not removed\n");
}
else else
printf("System OpenGL library NOT released\n"); printf("System OpenGL library NOT released\n");
this->hOpenGL = nullptr; return false;
} }
return true;
} }
bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const
{ {
// printf("is_version_greater_or_equal_to, version: %s\n", version.c_str()); // printf("is_version_greater_or_equal_to, version: %s\n", version.c_str());
@@ -85,10 +79,8 @@ public:
boost::split(tokens, version, boost::is_any_of(" "), boost::token_compress_on); boost::split(tokens, version, boost::is_any_of(" "), boost::token_compress_on);
if (tokens.empty()) if (tokens.empty())
return false; return false;
std::vector<std::string> numbers; std::vector<std::string> numbers;
boost::split(numbers, tokens[0], boost::is_any_of("."), boost::token_compress_on); boost::split(numbers, tokens[0], boost::is_any_of("."), boost::token_compress_on);
unsigned int gl_major = 0; unsigned int gl_major = 0;
unsigned int gl_minor = 0; unsigned int gl_minor = 0;
if (numbers.size() > 0) if (numbers.size() > 0)
@@ -103,10 +95,8 @@ public:
else else
return gl_minor >= minor; return gl_minor >= minor;
} }
protected: protected:
static bool message_pump_exit; static bool message_pump_exit;
void check(HWND hWnd) void check(HWND hWnd)
{ {
hOpenGL = LoadLibraryExW(L"opengl32.dll", nullptr, 0); hOpenGL = LoadLibraryExW(L"opengl32.dll", nullptr, 0);
@@ -114,22 +104,18 @@ protected:
printf("Failed loading the system opengl32.dll\n"); printf("Failed loading the system opengl32.dll\n");
return; return;
} }
typedef HGLRC(WINAPI* Func_wglCreateContext)(HDC);
typedef HGLRC (WINAPI *Func_wglCreateContext)(HDC); typedef BOOL(WINAPI* Func_wglMakeCurrent)(HDC, HGLRC);
typedef BOOL (WINAPI *Func_wglMakeCurrent )(HDC, HGLRC); typedef BOOL(WINAPI* Func_wglDeleteContext)(HGLRC);
typedef BOOL (WINAPI *Func_wglDeleteContext)(HGLRC); typedef GLubyte* (WINAPI* Func_glGetString)(GLenum);
typedef GLubyte* (WINAPI *Func_glGetString )(GLenum);
Func_wglCreateContext wglCreateContext = (Func_wglCreateContext)GetProcAddress(hOpenGL, "wglCreateContext"); Func_wglCreateContext wglCreateContext = (Func_wglCreateContext)GetProcAddress(hOpenGL, "wglCreateContext");
Func_wglMakeCurrent wglMakeCurrent = (Func_wglMakeCurrent) GetProcAddress(hOpenGL, "wglMakeCurrent"); Func_wglMakeCurrent wglMakeCurrent = (Func_wglMakeCurrent)GetProcAddress(hOpenGL, "wglMakeCurrent");
Func_wglDeleteContext wglDeleteContext = (Func_wglDeleteContext)GetProcAddress(hOpenGL, "wglDeleteContext"); Func_wglDeleteContext wglDeleteContext = (Func_wglDeleteContext)GetProcAddress(hOpenGL, "wglDeleteContext");
Func_glGetString glGetString = (Func_glGetString) GetProcAddress(hOpenGL, "glGetString"); Func_glGetString glGetString = (Func_glGetString)GetProcAddress(hOpenGL, "glGetString");
if (wglCreateContext == nullptr || wglMakeCurrent == nullptr || wglDeleteContext == nullptr || glGetString == nullptr) { if (wglCreateContext == nullptr || wglMakeCurrent == nullptr || wglDeleteContext == nullptr || glGetString == nullptr) {
printf("Failed loading the system opengl32.dll: The library is invalid.\n"); printf("Failed loading the system opengl32.dll: The library is invalid.\n");
return; return;
} }
PIXELFORMATDESCRIPTOR pfd = PIXELFORMATDESCRIPTOR pfd =
{ {
sizeof(PIXELFORMATDESCRIPTOR), sizeof(PIXELFORMATDESCRIPTOR),
@@ -149,7 +135,6 @@ protected:
0, 0,
0, 0, 0 0, 0, 0
}; };
HDC ourWindowHandleToDeviceContext = ::GetDC(hWnd); HDC ourWindowHandleToDeviceContext = ::GetDC(hWnd);
// Gdi32.dll // Gdi32.dll
int letWindowsChooseThisPixelFormat = ::ChoosePixelFormat(ourWindowHandleToDeviceContext, &pfd); int letWindowsChooseThisPixelFormat = ::ChoosePixelFormat(ourWindowHandleToDeviceContext, &pfd);
@@ -159,7 +144,7 @@ protected:
HGLRC glcontext = wglCreateContext(ourWindowHandleToDeviceContext); HGLRC glcontext = wglCreateContext(ourWindowHandleToDeviceContext);
wglMakeCurrent(ourWindowHandleToDeviceContext, glcontext); wglMakeCurrent(ourWindowHandleToDeviceContext, glcontext);
// Opengl32.dll // Opengl32.dll
const char *data = (const char*)glGetString(GL_VERSION); const char* data = (const char*)glGetString(GL_VERSION);
if (data != nullptr) if (data != nullptr)
this->version = data; this->version = data;
// printf("check -version: %s\n", version.c_str()); // printf("check -version: %s\n", version.c_str());
@@ -177,15 +162,14 @@ protected:
::ReleaseDC(hWnd, ourWindowHandleToDeviceContext); ::ReleaseDC(hWnd, ourWindowHandleToDeviceContext);
this->success = true; this->success = true;
} }
static LRESULT CALLBACK supports_opengl2_wndproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) static LRESULT CALLBACK supports_opengl2_wndproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{ {
switch(message) switch (message)
{ {
case WM_CREATE: case WM_CREATE:
{ {
CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT*>(lParam); CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
OpenGLVersionCheck *ogl_data = reinterpret_cast<OpenGLVersionCheck*>(pCreate->lpCreateParams); OpenGLVersionCheck* ogl_data = reinterpret_cast<OpenGLVersionCheck*>(pCreate->lpCreateParams);
ogl_data->check(hWnd); ogl_data->check(hWnd);
DestroyWindow(hWnd); DestroyWindow(hWnd);
return 0; return 0;
@@ -198,113 +182,108 @@ protected:
} }
} }
}; };
bool OpenGLVersionCheck::message_pump_exit = false; bool OpenGLVersionCheck::message_pump_exit = false;
#endif /* SLIC3R_GUI */ #endif /* SLIC3R_GUI */
extern "C" { extern "C" {
typedef int (__stdcall *Slic3rMainFunc)(int argc, wchar_t **argv); typedef int(__stdcall* Slic3rMainFunc)(int argc, wchar_t** argv);
Slic3rMainFunc qidistu_main = nullptr; Slic3rMainFunc qidistu_main = nullptr;
} }
extern "C" { extern "C" {
#ifdef SLIC3R_WRAPPER_NOCONSOLE #ifdef SLIC3R_WRAPPER_NOCONSOLE
int APIENTRY wWinMain(HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */, PWSTR /* lpCmdLine */, int /* nCmdShow */) int APIENTRY wWinMain(HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */, PWSTR /* lpCmdLine */, int /* nCmdShow */)
{ {
int argc; int argc;
wchar_t **argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
#else #else
int wmain(int argc, wchar_t **argv) int wmain(int argc, wchar_t** argv)
{ {
#endif #endif
// Allow the asserts to open message box, such message box allows to ignore the assert and continue with the application. // Allow the asserts to open message box, such message box allows to ignore the assert and continue with the application.
// Without this call, the seemingly same message box is being opened by the abort() function, but that is too late and // Without this call, the seemingly same message box is being opened by the abort() function, but that is too late and
// the application will be killed even if "Ignore" button is pressed. // the application will be killed even if "Ignore" button is pressed.
_set_error_mode(_OUT_TO_MSGBOX); _set_error_mode(_OUT_TO_MSGBOX);
std::vector<wchar_t*> argv_extended;
std::vector<wchar_t*> argv_extended; argv_extended.emplace_back(argv[0]);
argv_extended.emplace_back(argv[0]);
#ifdef SLIC3R_WRAPPER_GCODEVIEWER #ifdef SLIC3R_WRAPPER_GCODEVIEWER
wchar_t gcodeviewer_param[] = L"--gcodeviewer"; wchar_t gcodeviewer_param[] = L"--gcodeviewer";
argv_extended.emplace_back(gcodeviewer_param); argv_extended.emplace_back(gcodeviewer_param);
#endif /* SLIC3R_WRAPPER_GCODEVIEWER */ #endif /* SLIC3R_WRAPPER_GCODEVIEWER */
#ifdef SLIC3R_GUI #ifdef SLIC3R_GUI
// Here one may push some additional parameters based on the wrapper type. // Here one may push some additional parameters based on the wrapper type.
bool force_mesa = false; bool force_mesa = false;
#endif /* SLIC3R_GUI */ #endif /* SLIC3R_GUI */
for (int i = 1; i < argc; ++ i) { for (int i = 1; i < argc; ++i) {
#ifdef SLIC3R_GUI #ifdef SLIC3R_GUI
if (wcscmp(argv[i], L"--sw-renderer") == 0) if (wcscmp(argv[i], L"--sw-renderer") == 0)
force_mesa = true; force_mesa = true;
else if (wcscmp(argv[i], L"--no-sw-renderer") == 0) else if (wcscmp(argv[i], L"--no-sw-renderer") == 0)
force_mesa = false; force_mesa = false;
#endif /* SLIC3R_GUI */ #endif /* SLIC3R_GUI */
argv_extended.emplace_back(argv[i]); argv_extended.emplace_back(argv[i]);
} }
argv_extended.emplace_back(nullptr); argv_extended.emplace_back(nullptr);
#ifdef SLIC3R_GUI #ifdef SLIC3R_GUI
OpenGLVersionCheck opengl_version_check; OpenGLVersionCheck opengl_version_check;
bool load_mesa = bool load_mesa =
// Forced from the command line. // Forced from the command line.
force_mesa || force_mesa ||
// Try to load the default OpenGL driver and test its context version. // Try to load the default OpenGL driver and test its context version.
! opengl_version_check.load_opengl_dll() || ! opengl_version_check.is_version_greater_or_equal_to(2, 0); !opengl_version_check.load_opengl_dll() || !opengl_version_check.is_version_greater_or_equal_to(3, 2);
#endif /* SLIC3R_GUI */ #endif /* SLIC3R_GUI */
wchar_t path_to_exe[MAX_PATH + 1] = { 0 };
wchar_t path_to_exe[MAX_PATH + 1] = { 0 }; ::GetModuleFileNameW(nullptr, path_to_exe, MAX_PATH);
::GetModuleFileNameW(nullptr, path_to_exe, MAX_PATH); wchar_t drive[_MAX_DRIVE];
wchar_t drive[_MAX_DRIVE]; wchar_t dir[_MAX_DIR];
wchar_t dir[_MAX_DIR]; wchar_t fname[_MAX_FNAME];
wchar_t fname[_MAX_FNAME]; wchar_t ext[_MAX_EXT];
wchar_t ext[_MAX_EXT]; _wsplitpath(path_to_exe, drive, dir, fname, ext);
_wsplitpath(path_to_exe, drive, dir, fname, ext); _wmakepath(path_to_exe, drive, dir, nullptr, nullptr);
_wmakepath(path_to_exe, drive, dir, nullptr, nullptr);
#ifdef SLIC3R_GUI #ifdef SLIC3R_GUI
// https://wiki.qt.io/Cross_compiling_Mesa_for_Windows // https://wiki.qt.io/Cross_compiling_Mesa_for_Windows
// http://download.qt.io/development_releases/prebuilt/llvmpipe/windows/ // http://download.qt.io/development_releases/prebuilt/llvmpipe/windows/
if (load_mesa) { if (load_mesa) {
opengl_version_check.unload_opengl_dll(); bool res = opengl_version_check.unload_opengl_dll();
wchar_t path_to_mesa[MAX_PATH + 1] = { 0 }; if (!res) {
wcscpy(path_to_mesa, path_to_exe); MessageBox(nullptr, L"Error:QIDIStudio was unable to automatically switch to MESA OpenGL library.",
wcscat(path_to_mesa, L"mesa\\opengl32.dll"); L"QIDIStudio Error", MB_OK);
printf("Loading MESA OpenGL library: %S\n", path_to_mesa); return -1;
HINSTANCE hInstance_OpenGL = LoadLibraryExW(path_to_mesa, nullptr, 0); }
if (hInstance_OpenGL == nullptr) { else {
printf("MESA OpenGL library was not loaded\n"); wchar_t path_to_mesa[MAX_PATH + 1] = { 0 };
} else wcscpy(path_to_mesa, path_to_exe);
printf("MESA OpenGL library was loaded sucessfully\n"); wcscat(path_to_mesa, L"mesa\\opengl32.dll");
} printf("Loading MESA OpenGL library: %S\n", path_to_mesa);
HINSTANCE hInstance_OpenGL = LoadLibraryExW(path_to_mesa, nullptr, 0);
if (hInstance_OpenGL == nullptr)
printf("MESA OpenGL library was not loaded\n");
else
printf("MESA OpenGL library was loaded sucessfully\n");
}
}
#endif /* SLIC3R_GUI */ #endif /* SLIC3R_GUI */
wchar_t path_to_slic3r[MAX_PATH + 1] = { 0 };
wcscpy(path_to_slic3r, path_to_exe);
wchar_t path_to_slic3r[MAX_PATH + 1] = { 0 }; wcscat(path_to_slic3r, L"QIDIStudio.dll");
wcscpy(path_to_slic3r, path_to_exe); // printf("Loading Slic3r library: %S\n", path_to_slic3r);
wcscat(path_to_slic3r, L"QIDIStudio.dll"); HINSTANCE hInstance_Slic3r = LoadLibraryExW(path_to_slic3r, nullptr, 0);
// printf("Loading Slic3r library: %S\n", path_to_slic3r); if (hInstance_Slic3r == nullptr) {
HINSTANCE hInstance_Slic3r = LoadLibraryExW(path_to_slic3r, nullptr, 0); printf("QIDIStudio.dll was not loaded, error=%d\n", GetLastError());
if (hInstance_Slic3r == nullptr) { return -1;
printf("QIDIStudio.dll was not loaded, error=%d\n", GetLastError()); }
return -1; // resolve function address here
} qidistu_main = (Slic3rMainFunc)GetProcAddress(hInstance_Slic3r,
// resolve function address here
qidistu_main = (Slic3rMainFunc)GetProcAddress(hInstance_Slic3r,
#ifdef _WIN64 #ifdef _WIN64
// there is just a single calling conversion, therefore no mangling of the function name. // there is just a single calling conversion, therefore no mangling of the function name.
"qidistu_main" "qidistu_main"
#else // stdcall calling convention declaration #else // stdcall calling convention declaration
"_qidistu_main@8" "_qidistu_main@8"
#endif #endif
); );
if (qidistu_main == nullptr) { if (qidistu_main == nullptr) {
printf("could not locate the function qidistu_main in QIDIStudio.dll\n"); printf("could not locate the function bambustu_main in QIDIStudio.dll\n");
return -1; return -1;
}
// argc minus the trailing nullptr of the argv
return qidistu_main((int)argv_extended.size() - 1, argv_extended.data());
} }
// argc minus the trailing nullptr of the argv
return qidistu_main((int)argv_extended.size() - 1, argv_extended.data());
}
} }

View File

@@ -28,7 +28,7 @@
#include <stddef.h> #include <stddef.h>
#include <vector> #include <vector>
#include <Eigen/Geometry> #include <Eigen/Geometry>
// Size of the binary STL header, free form. // Size of the binary STL header, free form.
#define LABEL_SIZE 80 #define LABEL_SIZE 80
@@ -178,7 +178,7 @@ struct FaceProperty
std::string to_string() const std::string to_string() const
{ {
std::string str; std::string str;
// skip normal type facet to improve performance // skip normal type facet to improve performance
if (type > eNormal && type < eMaxNumFaceTypes) { if (type > eNormal && type < eMaxNumFaceTypes) {
str += std::to_string(type); str += std::to_string(type);
if (area != 0.f) if (area != 0.f)
@@ -230,7 +230,16 @@ struct indexed_triangle_set
size_t memsize() const { size_t memsize() const {
return sizeof(*this) + (sizeof(stl_triangle_vertex_indices) + sizeof(FaceProperty)) * indices.size() + sizeof(stl_vertex) * vertices.size(); return sizeof(*this) + (sizeof(stl_triangle_vertex_indices) + sizeof(FaceProperty)) * indices.size() + sizeof(stl_vertex) * vertices.size();
} }
void add_indice(int f0, int f1, int f2, bool check_pts_size = false)
{
if (f0 < 0 || f1 < 0 || f2 < 0) { return; }
if (check_pts_size) {
auto pts_size = vertices.size();
if (f0 < pts_size && f1 < pts_size && f2 < pts_size) { indices.emplace_back(f0, f1, f2); }
} else {
indices.emplace_back(f0, f1, f2);
}
}
std::vector<stl_triangle_vertex_indices> indices; std::vector<stl_triangle_vertex_indices> indices;
std::vector<stl_vertex> vertices; std::vector<stl_vertex> vertices;
std::vector<FaceProperty> properties; std::vector<FaceProperty> properties;

View File

@@ -121,7 +121,7 @@ static FILE *stl_open_count_facets(stl_file *stl, const char *file, unsigned int
fclose(fp); fclose(fp);
return nullptr; return nullptr;
} }
// Find the number of facets. // Find the number of facets.
char linebuf[100]; char linebuf[100];
int num_lines = 1; int num_lines = 1;
@@ -136,9 +136,9 @@ static FILE *stl_open_count_facets(stl_file *stl, const char *file, unsigned int
} }
rewind(fp); rewind(fp);
// Get the header. // Get the header.
int i = 0; unsigned int i = 0;
for (; i < custom_header_length && (stl->stats.header[i] = getc(fp)) != '\n'; ++ i) ; for (; i < custom_header_length && (stl->stats.header[i] = getc(fp)) != '\n'; ++ i) ;
stl->stats.header[i] = '\0'; // Lose the '\n' stl->stats.header[i] = '\0'; // Lose the '\n'
stl->stats.header[custom_header_length] = '\0'; stl->stats.header[custom_header_length] = '\0';
@@ -225,10 +225,10 @@ static bool stl_read(stl_file *stl, FILE *fp, int first_facet, bool first, Impor
} }
catch (...){ catch (...){
} }
rewind(fp); rewind(fp);
} }
char normal_buf[3][32]; char normal_buf[3][32];
@@ -247,7 +247,7 @@ static bool stl_read(stl_file *stl, FILE *fp, int first_facet, bool first, Impor
stl_facet facet; stl_facet facet;
if (stl->stats.type == binary) { if (stl->stats.type == binary) {
// Read a single facet from a binary .STL file. We assume little-endian architecture! // Read a single facet from a binary .STL file. We assume little-endian architecture!
if (fread(&facet, 1, SIZEOF_STL_FACET, fp) != SIZEOF_STL_FACET) if (fread(&facet, 1, SIZEOF_STL_FACET, fp) != SIZEOF_STL_FACET)

View File

@@ -1,5 +1,5 @@
option(SLIC3R_ENC_CHECK "Verify encoding of source files" 1) option(SLIC3R_ENC_CHECK "Verify encoding of source files" 0)
if (IS_CROSS_COMPILE) if (IS_CROSS_COMPILE)
# Force disable due to cross compilation. This fact is already printed on cli for users # Force disable due to cross compilation. This fact is already printed on cli for users

File diff suppressed because it is too large Load Diff

View File

@@ -10,16 +10,10 @@
#ifndef CLIPPER_ENGINE_H #ifndef CLIPPER_ENGINE_H
#define CLIPPER_ENGINE_H #define CLIPPER_ENGINE_H
constexpr auto CLIPPER2_VERSION = "1.0.6"; #include "clipper2/clipper.core.h"
#include <cstdlib>
#include <queue> #include <queue>
#include <stdexcept>
#include <vector>
#include <functional> #include <functional>
#include <cstddef> #include <memory>
#include <cstdint>
#include "clipper.core.h"
namespace Clipper2Lib { namespace Clipper2Lib {
@@ -29,15 +23,16 @@ namespace Clipper2Lib {
struct Vertex; struct Vertex;
struct LocalMinima; struct LocalMinima;
struct OutRec; struct OutRec;
struct Joiner; struct HorzSegment;
//Note: all clipping operations except for Difference are commutative. //Note: all clipping operations except for Difference are commutative.
enum class ClipType { None, Intersection, Union, Difference, Xor }; enum class ClipType { NoClip, Intersection, Union, Difference, Xor };
enum class PathType { Subject, Clip }; enum class PathType { Subject, Clip };
enum class JoinWith { NoJoin, Left, Right };
enum class VertexFlags : uint32_t { enum class VertexFlags : uint32_t {
None = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8 Empty = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8
}; };
constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b) constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b)
@@ -54,7 +49,7 @@ namespace Clipper2Lib {
Point64 pt; Point64 pt;
Vertex* next = nullptr; Vertex* next = nullptr;
Vertex* prev = nullptr; Vertex* prev = nullptr;
VertexFlags flags = VertexFlags::None; VertexFlags flags = VertexFlags::Empty;
}; };
struct OutPt { struct OutPt {
@@ -62,7 +57,7 @@ namespace Clipper2Lib {
OutPt* next = nullptr; OutPt* next = nullptr;
OutPt* prev = nullptr; OutPt* prev = nullptr;
OutRec* outrec; OutRec* outrec;
Joiner* joiner = nullptr; HorzSegment* horz = nullptr;
OutPt(const Point64& pt_, OutRec* outrec_): pt(pt_), outrec(outrec_) { OutPt(const Point64& pt_, OutRec* outrec_): pt(pt_), outrec(outrec_) {
next = this; next = this;
@@ -84,15 +79,21 @@ namespace Clipper2Lib {
struct OutRec { struct OutRec {
size_t idx = 0; size_t idx = 0;
OutRec* owner = nullptr; OutRec* owner = nullptr;
OutRecList* splits = nullptr;
Active* front_edge = nullptr; Active* front_edge = nullptr;
Active* back_edge = nullptr; Active* back_edge = nullptr;
OutPt* pts = nullptr; OutPt* pts = nullptr;
PolyPath* polypath = nullptr; PolyPath* polypath = nullptr;
OutRecList* splits = nullptr;
OutRec* recursive_split = nullptr;
Rect64 bounds = {}; Rect64 bounds = {};
Path64 path; Path64 path;
bool is_open = false; bool is_open = false;
~OutRec() { if (splits) delete splits; };
~OutRec() {
if (splits) delete splits;
// nb: don't delete the split pointers
// as these are owned by ClipperBase's outrec_list_
};
}; };
/////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////
@@ -124,6 +125,7 @@ namespace Clipper2Lib {
Vertex* vertex_top = nullptr; Vertex* vertex_top = nullptr;
LocalMinima* local_min = nullptr; // the bottom of an edge 'bound' (also Vatti) LocalMinima* local_min = nullptr; // the bottom of an edge 'bound' (also Vatti)
bool is_left_bound = false; bool is_left_bound = false;
JoinWith join_with = JoinWith::NoJoin;
}; };
struct LocalMinima { struct LocalMinima {
@@ -138,26 +140,58 @@ namespace Clipper2Lib {
Point64 pt; Point64 pt;
Active* edge1; Active* edge1;
Active* edge2; Active* edge2;
IntersectNode() : pt(Point64(0, 0)), edge1(NULL), edge2(NULL) {} IntersectNode() : pt(Point64(0,0)), edge1(NULL), edge2(NULL) {}
IntersectNode(Active* e1, Active* e2, Point64& pt_) : IntersectNode(Active* e1, Active* e2, Point64& pt_) :
pt(pt_), edge1(e1), edge2(e2) pt(pt_), edge1(e1), edge2(e2) {}
{ };
}
struct HorzSegment {
OutPt* left_op;
OutPt* right_op = nullptr;
bool left_to_right = true;
HorzSegment() : left_op(nullptr) { }
explicit HorzSegment(OutPt* op) : left_op(op) { }
};
struct HorzJoin {
OutPt* op1 = nullptr;
OutPt* op2 = nullptr;
HorzJoin() {};
explicit HorzJoin(OutPt* ltr, OutPt* rtl) : op1(ltr), op2(rtl) { }
}; };
#ifdef USINGZ #ifdef USINGZ
typedef std::function<void(const Point64& e1bot, const Point64& e1top, typedef std::function<void(const Point64& e1bot, const Point64& e1top,
const Point64& e2bot, const Point64& e2top, Point64& pt)> ZCallback64; const Point64& e2bot, const Point64& e2top, Point64& pt)> ZCallback64;
typedef std::function<void(const PointD& e1bot, const PointD& e1top, typedef std::function<void(const PointD& e1bot, const PointD& e1top,
const PointD& e2bot, const PointD& e2top, PointD& pt)> ZCallbackD; const PointD& e2bot, const PointD& e2top, PointD& pt)> ZCallbackD;
#endif #endif
typedef std::vector<HorzSegment> HorzSegmentList;
typedef std::unique_ptr<LocalMinima> LocalMinima_ptr;
typedef std::vector<LocalMinima_ptr> LocalMinimaList;
typedef std::vector<IntersectNode> IntersectNodeList;
// ReuseableDataContainer64 ------------------------------------------------
class ReuseableDataContainer64 {
private:
friend class ClipperBase;
LocalMinimaList minima_list_;
std::vector<Vertex*> vertex_lists_;
void AddLocMin(Vertex& vert, PathType polytype, bool is_open);
public:
virtual ~ReuseableDataContainer64();
void Clear();
void AddPaths(const Paths64& paths, PathType polytype, bool is_open);
};
// ClipperBase ------------------------------------------------------------- // ClipperBase -------------------------------------------------------------
class ClipperBase { class ClipperBase {
private: private:
ClipType cliptype_ = ClipType::None; ClipType cliptype_ = ClipType::NoClip;
FillRule fillrule_ = FillRule::EvenOdd; FillRule fillrule_ = FillRule::EvenOdd;
FillRule fillpos = FillRule::Positive; FillRule fillpos = FillRule::Positive;
int64_t bot_y_ = 0; int64_t bot_y_ = 0;
@@ -165,21 +199,21 @@ namespace Clipper2Lib {
bool using_polytree_ = false; bool using_polytree_ = false;
Active* actives_ = nullptr; Active* actives_ = nullptr;
Active *sel_ = nullptr; Active *sel_ = nullptr;
Joiner *horz_joiners_ = nullptr; LocalMinimaList minima_list_; //pointers in case of memory reallocs
std::vector<LocalMinima*> minima_list_; //pointers in case of memory reallocs LocalMinimaList::iterator current_locmin_iter_;
std::vector<LocalMinima*>::iterator current_locmin_iter_;
std::vector<Vertex*> vertex_lists_; std::vector<Vertex*> vertex_lists_;
std::priority_queue<int64_t> scanline_list_; std::priority_queue<int64_t> scanline_list_;
std::vector<IntersectNode> intersect_nodes_; IntersectNodeList intersect_nodes_;
std::vector<Joiner*> joiner_list_; //pointers in case of memory reallocs HorzSegmentList horz_seg_list_;
std::vector<HorzJoin> horz_join_list_;
void Reset(); void Reset();
void InsertScanline(int64_t y); inline void InsertScanline(int64_t y);
bool PopScanline(int64_t &y); inline bool PopScanline(int64_t &y);
bool PopLocalMinima(int64_t y, LocalMinima *&local_minima); inline bool PopLocalMinima(int64_t y, LocalMinima*& local_minima);
void DisposeAllOutRecs(); void DisposeAllOutRecs();
void DisposeVerticesAndLocalMinima(); void DisposeVerticesAndLocalMinima();
void DeleteEdges(Active*& e); void DeleteEdges(Active*& e);
void AddLocMin(Vertex &vert, PathType polytype, bool is_open); inline void AddLocMin(Vertex &vert, PathType polytype, bool is_open);
bool IsContributingClosed(const Active &e) const; bool IsContributingClosed(const Active &e) const;
inline bool IsContributingOpen(const Active &e) const; inline bool IsContributingOpen(const Active &e) const;
void SetWindCountForClosedPathEdge(Active &edge); void SetWindCountForClosedPathEdge(Active &edge);
@@ -190,7 +224,7 @@ namespace Clipper2Lib {
inline bool PopHorz(Active *&e); inline bool PopHorz(Active *&e);
inline OutPt* StartOpenPath(Active &e, const Point64& pt); inline OutPt* StartOpenPath(Active &e, const Point64& pt);
inline void UpdateEdgeIntoAEL(Active *e); inline void UpdateEdgeIntoAEL(Active *e);
OutPt* IntersectEdges(Active &e1, Active &e2, const Point64& pt); void IntersectEdges(Active &e1, Active &e2, const Point64& pt);
inline void DeleteFromAEL(Active &e); inline void DeleteFromAEL(Active &e);
inline void AdjustCurrXAndCopyToSEL(const int64_t top_y); inline void AdjustCurrXAndCopyToSEL(const int64_t top_y);
void DoIntersections(const int64_t top_y); void DoIntersections(const int64_t top_y);
@@ -198,38 +232,41 @@ namespace Clipper2Lib {
bool BuildIntersectList(const int64_t top_y); bool BuildIntersectList(const int64_t top_y);
void ProcessIntersectList(); void ProcessIntersectList();
void SwapPositionsInAEL(Active& edge1, Active& edge2); void SwapPositionsInAEL(Active& edge1, Active& edge2);
OutRec* NewOutRec();
OutPt* AddOutPt(const Active &e, const Point64& pt); OutPt* AddOutPt(const Active &e, const Point64& pt);
OutPt* AddLocalMinPoly(Active &e1, Active &e2, OutPt* AddLocalMinPoly(Active &e1, Active &e2,
const Point64& pt, bool is_new = false); const Point64& pt, bool is_new = false);
OutPt* AddLocalMaxPoly(Active &e1, Active &e2, const Point64& pt); OutPt* AddLocalMaxPoly(Active &e1, Active &e2, const Point64& pt);
void DoHorizontal(Active &horz); void DoHorizontal(Active &horz);
bool ResetHorzDirection(const Active &horz, const Active *max_pair, bool ResetHorzDirection(const Active &horz, const Vertex* max_vertex,
int64_t &horz_left, int64_t &horz_right); int64_t &horz_left, int64_t &horz_right);
void DoTopOfScanbeam(const int64_t top_y); void DoTopOfScanbeam(const int64_t top_y);
Active *DoMaxima(Active &e); Active *DoMaxima(Active &e);
void JoinOutrecPaths(Active &e1, Active &e2); void JoinOutrecPaths(Active &e1, Active &e2);
void CompleteSplit(OutPt* op1, OutPt* op2, OutRec& outrec);
bool ValidateClosedPathEx(OutPt*& outrec);
void CleanCollinear(OutRec* outrec);
void FixSelfIntersects(OutRec* outrec); void FixSelfIntersects(OutRec* outrec);
void DoSplitOp(OutRec* outRec, OutPt* splitOp); void DoSplitOp(OutRec* outRec, OutPt* splitOp);
Joiner* GetHorzTrialParent(const OutPt* op);
bool OutPtInTrialHorzList(OutPt* op); inline void AddTrialHorzJoin(OutPt* op);
void SafeDisposeOutPts(OutPt*& op); void ConvertHorzSegsToJoins();
void SafeDeleteOutPtJoiners(OutPt* op); void ProcessHorzJoins();
void AddTrialHorzJoin(OutPt* op);
void DeleteTrialHorzJoin(OutPt* op); void Split(Active& e, const Point64& pt);
void ConvertHorzTrialsToJoins(); inline void CheckJoinLeft(Active& e,
void AddJoin(OutPt* op1, OutPt* op2); const Point64& pt, bool check_curr_x = false);
void DeleteJoin(Joiner* joiner); inline void CheckJoinRight(Active& e,
void ProcessJoinerList(); const Point64& pt, bool check_curr_x = false);
OutRec* ProcessJoin(Joiner* joiner);
protected: protected:
bool preserve_collinear_ = true;
bool reverse_solution_ = false;
int error_code_ = 0;
bool has_open_paths_ = false; bool has_open_paths_ = false;
bool succeeded_ = true; bool succeeded_ = true;
std::vector<OutRec*> outrec_list_; //pointers in case list memory reallocated OutRecList outrec_list_; //pointers in case list memory reallocated
bool ExecuteInternal(ClipType ct, FillRule ft, bool use_polytrees); bool ExecuteInternal(ClipType ct, FillRule ft, bool use_polytrees);
bool DeepCheckOwner(OutRec* outrec, OutRec* owner); void CleanCollinear(OutRec* outrec);
bool CheckBounds(OutRec* outrec);
bool CheckSplitOwner(OutRec* outrec, OutRecList* splits);
void RecursiveCheckOwners(OutRec* outrec, PolyPath* polypath);
#ifdef USINGZ #ifdef USINGZ
ZCallback64 zCallback_ = nullptr; ZCallback64 zCallback_ = nullptr;
void SetZ(const Active& e1, const Active& e2, Point64& pt); void SetZ(const Active& e1, const Active& e2, Point64& pt);
@@ -239,9 +276,16 @@ namespace Clipper2Lib {
void AddPaths(const Paths64& paths, PathType polytype, bool is_open); void AddPaths(const Paths64& paths, PathType polytype, bool is_open);
public: public:
virtual ~ClipperBase(); virtual ~ClipperBase();
bool PreserveCollinear = true; int ErrorCode() const { return error_code_; };
bool ReverseSolution = false; void PreserveCollinear(bool val) { preserve_collinear_ = val; };
bool PreserveCollinear() const { return preserve_collinear_;};
void ReverseSolution(bool val) { reverse_solution_ = val; };
bool ReverseSolution() const { return reverse_solution_; };
void Clear(); void Clear();
void AddReuseableData(const ReuseableDataContainer64& reuseable_data);
#ifdef USINGZ
int64_t DefaultZ = 0;
#endif
}; };
// PolyPath / PolyTree -------------------------------------------------------- // PolyPath / PolyTree --------------------------------------------------------
@@ -256,7 +300,7 @@ namespace Clipper2Lib {
PolyPath* parent_; PolyPath* parent_;
public: public:
PolyPath(PolyPath* parent = nullptr): parent_(parent){} PolyPath(PolyPath* parent = nullptr): parent_(parent){}
virtual ~PolyPath() { Clear(); }; virtual ~PolyPath() {};
//https://en.cppreference.com/w/cpp/language/rule_of_three //https://en.cppreference.com/w/cpp/language/rule_of_three
PolyPath(const PolyPath&) = delete; PolyPath(const PolyPath&) = delete;
PolyPath& operator=(const PolyPath&) = delete; PolyPath& operator=(const PolyPath&) = delete;
@@ -271,49 +315,58 @@ namespace Clipper2Lib {
virtual PolyPath* AddChild(const Path64& path) = 0; virtual PolyPath* AddChild(const Path64& path) = 0;
virtual void Clear() {}; virtual void Clear() = 0;
virtual size_t Count() const { return 0; } virtual size_t Count() const { return 0; }
const PolyPath* Parent() const { return parent_; } const PolyPath* Parent() const { return parent_; }
bool IsHole() const bool IsHole() const
{ {
const PolyPath* pp = parent_; unsigned lvl = Level();
bool is_hole = pp; //Even levels except level 0
while (pp) { return lvl && !(lvl & 1);
is_hole = !is_hole;
pp = pp->parent_;
}
return is_hole;
} }
}; };
typedef typename std::vector<std::unique_ptr<PolyPath64>> PolyPath64List;
typedef typename std::vector<std::unique_ptr<PolyPathD>> PolyPathDList;
class PolyPath64 : public PolyPath { class PolyPath64 : public PolyPath {
private: private:
std::vector<PolyPath64*> childs_; PolyPath64List childs_;
Path64 polygon_; Path64 polygon_;
typedef typename std::vector<PolyPath64*>::const_iterator pp64_itor;
public: public:
PolyPath64(PolyPath64* parent = nullptr) : PolyPath(parent) {} explicit PolyPath64(PolyPath64* parent = nullptr) : PolyPath(parent) {}
PolyPath64* operator [] (size_t index) { return static_cast<PolyPath64*>(childs_[index]); } explicit PolyPath64(PolyPath64* parent, const Path64& path) : PolyPath(parent) { polygon_ = path; }
pp64_itor begin() const { return childs_.cbegin(); }
pp64_itor end() const { return childs_.cend(); } ~PolyPath64() {
childs_.resize(0);
}
PolyPath64* operator [] (size_t index) const
{
return childs_[index].get(); //std::unique_ptr
}
PolyPath64* Child(size_t index) const
{
return childs_[index].get();
}
PolyPath64List::const_iterator begin() const { return childs_.cbegin(); }
PolyPath64List::const_iterator end() const { return childs_.cend(); }
PolyPath64* AddChild(const Path64& path) override PolyPath64* AddChild(const Path64& path) override
{ {
PolyPath64* result = new PolyPath64(this); return childs_.emplace_back(std::make_unique<PolyPath64>(this, path)).get();
childs_.push_back(result);
result->polygon_ = path;
return result;
} }
void Clear() override void Clear() override
{ {
for (const PolyPath64* child : childs_) delete child;
childs_.resize(0); childs_.resize(0);
} }
size_t Count() const override size_t Count() const override
{ {
return childs_.size(); return childs_.size();
} }
@@ -322,78 +375,73 @@ namespace Clipper2Lib {
double Area() const double Area() const
{ {
double result = Clipper2Lib::Area<int64_t>(polygon_); return std::accumulate(childs_.cbegin(), childs_.cend(),
for (const PolyPath64* child : childs_) Clipper2Lib::Area<int64_t>(polygon_),
result += child->Area(); [](double a, const auto& child) {return a + child->Area(); });
return result;
}
friend std::ostream& operator << (std::ostream& outstream, const PolyPath64& polypath)
{
const size_t level_indent = 4;
const size_t coords_per_line = 4;
const size_t last_on_line = coords_per_line - 1;
unsigned level = polypath.Level();
if (level > 0)
{
std::string level_padding;
level_padding.insert(0, (level - 1) * level_indent, ' ');
std::string caption = polypath.IsHole() ? "Hole " : "Outer Polygon ";
std::string childs = polypath.Count() == 1 ? " child" : " children";
outstream << level_padding.c_str() << caption << "with " << polypath.Count() << childs << std::endl;
outstream << level_padding;
size_t i = 0, highI = polypath.Polygon().size() - 1;
for (; i < highI; ++i)
{
outstream << polypath.Polygon()[i] << ' ';
if ((i % coords_per_line) == last_on_line)
outstream << std::endl << level_padding;
}
if (highI > 0) outstream << polypath.Polygon()[i];
outstream << std::endl;
}
for (auto child : polypath)
outstream << *child;
return outstream;
} }
}; };
class PolyPathD : public PolyPath { class PolyPathD : public PolyPath {
private: private:
std::vector<PolyPathD*> childs_; PolyPathDList childs_;
double inv_scale_; double scale_;
PathD polygon_; PathD polygon_;
typedef typename std::vector<PolyPathD*>::const_iterator ppD_itor;
public: public:
PolyPathD(PolyPathD* parent = nullptr) : PolyPath(parent) explicit PolyPathD(PolyPathD* parent = nullptr) : PolyPath(parent)
{ {
inv_scale_ = parent ? parent->inv_scale_ : 1.0; scale_ = parent ? parent->scale_ : 1.0;
} }
PolyPathD* operator [] (size_t index)
{
return static_cast<PolyPathD*>(childs_[index]);
}
ppD_itor begin() const { return childs_.cbegin(); }
ppD_itor end() const { return childs_.cend(); }
void SetInvScale(double value) { inv_scale_ = value; } explicit PolyPathD(PolyPathD* parent, const Path64& path) : PolyPath(parent)
double InvScale() { return inv_scale_; } {
scale_ = parent ? parent->scale_ : 1.0;
int error_code = 0;
polygon_ = ScalePath<double, int64_t>(path, scale_, error_code);
}
explicit PolyPathD(PolyPathD* parent, const PathD& path) : PolyPath(parent)
{
scale_ = parent ? parent->scale_ : 1.0;
polygon_ = path;
}
~PolyPathD() {
childs_.resize(0);
}
PolyPathD* operator [] (size_t index) const
{
return childs_[index].get();
}
PolyPathD* Child(size_t index) const
{
return childs_[index].get();
}
PolyPathDList::const_iterator begin() const { return childs_.cbegin(); }
PolyPathDList::const_iterator end() const { return childs_.cend(); }
void SetScale(double value) { scale_ = value; }
double Scale() const { return scale_; }
PolyPathD* AddChild(const Path64& path) override PolyPathD* AddChild(const Path64& path) override
{ {
PolyPathD* result = new PolyPathD(this); return childs_.emplace_back(std::make_unique<PolyPathD>(this, path)).get();
childs_.push_back(result); }
result->polygon_ = ScalePath<double, int64_t>(path, inv_scale_);
return result; PolyPathD* AddChild(const PathD& path)
{
return childs_.emplace_back(std::make_unique<PolyPathD>(this, path)).get();
} }
void Clear() override void Clear() override
{ {
for (const PolyPathD* child : childs_) delete child;
childs_.resize(0); childs_.resize(0);
} }
size_t Count() const override size_t Count() const override
{ {
return childs_.size(); return childs_.size();
} }
@@ -402,10 +450,9 @@ namespace Clipper2Lib {
double Area() const double Area() const
{ {
double result = Clipper2Lib::Area<double>(polygon_); return std::accumulate(childs_.begin(), childs_.end(),
for (const PolyPathD* child : childs_) Clipper2Lib::Area<double>(polygon_),
result += child->Area(); [](double a, const auto& child) {return a + child->Area(); });
return result;
} }
}; };
@@ -445,7 +492,7 @@ namespace Clipper2Lib {
closed_paths.clear(); closed_paths.clear();
open_paths.clear(); open_paths.clear();
if (ExecuteInternal(clip_type, fill_rule, false)) if (ExecuteInternal(clip_type, fill_rule, false))
BuildPaths64(closed_paths, &open_paths); BuildPaths64(closed_paths, &open_paths);
CleanUp(); CleanUp();
return succeeded_; return succeeded_;
} }
@@ -474,14 +521,14 @@ namespace Clipper2Lib {
private: private:
double scale_ = 1.0, invScale_ = 1.0; double scale_ = 1.0, invScale_ = 1.0;
#ifdef USINGZ #ifdef USINGZ
ZCallbackD zCallback_ = nullptr; ZCallbackD zCallbackD_ = nullptr;
#endif #endif
void BuildPathsD(PathsD& solutionClosed, PathsD* solutionOpen); void BuildPathsD(PathsD& solutionClosed, PathsD* solutionOpen);
void BuildTreeD(PolyPathD& polytree, PathsD& open_paths); void BuildTreeD(PolyPathD& polytree, PathsD& open_paths);
public: public:
explicit ClipperD(int precision = 2) : ClipperBase() explicit ClipperD(int precision = 2) : ClipperBase()
{ {
CheckPrecision(precision); CheckPrecisionRange(precision, error_code_);
// to optimize scaling / descaling precision // to optimize scaling / descaling precision
// set the scale to a power of double's radix (2) (#25) // set the scale to a power of double's radix (2) (#25)
scale_ = std::pow(std::numeric_limits<double>::radix, scale_ = std::pow(std::numeric_limits<double>::radix,
@@ -490,7 +537,7 @@ namespace Clipper2Lib {
} }
#ifdef USINGZ #ifdef USINGZ
void SetZCallback(ZCallbackD cb) { zCallback_ = cb; }; void SetZCallback(ZCallbackD cb) { zCallbackD_ = cb; };
void ZCB(const Point64& e1bot, const Point64& e1top, void ZCB(const Point64& e1bot, const Point64& e1top,
const Point64& e2bot, const Point64& e2top, Point64& pt) const Point64& e2bot, const Point64& e2top, Point64& pt)
@@ -504,13 +551,13 @@ namespace Clipper2Lib {
PointD e1t = PointD(e1top) * invScale_; PointD e1t = PointD(e1top) * invScale_;
PointD e2b = PointD(e2bot) * invScale_; PointD e2b = PointD(e2bot) * invScale_;
PointD e2t = PointD(e2top) * invScale_; PointD e2t = PointD(e2top) * invScale_;
zCallback_(e1b,e1t, e2b, e2t, tmp); zCallbackD_(e1b,e1t, e2b, e2t, tmp);
pt.z = tmp.z; // only update 'z' pt.z = tmp.z; // only update 'z'
}; };
void CheckCallback() void CheckCallback()
{ {
if(zCallback_) if(zCallbackD_)
// if the user defined float point callback has been assigned // if the user defined float point callback has been assigned
// then assign the proxy callback function // then assign the proxy callback function
ClipperBase::zCallback_ = ClipperBase::zCallback_ =
@@ -525,17 +572,17 @@ namespace Clipper2Lib {
void AddSubject(const PathsD& subjects) void AddSubject(const PathsD& subjects)
{ {
AddPaths(ScalePaths<int64_t, double>(subjects, scale_), PathType::Subject, false); AddPaths(ScalePaths<int64_t, double>(subjects, scale_, error_code_), PathType::Subject, false);
} }
void AddOpenSubject(const PathsD& open_subjects) void AddOpenSubject(const PathsD& open_subjects)
{ {
AddPaths(ScalePaths<int64_t, double>(open_subjects, scale_), PathType::Subject, true); AddPaths(ScalePaths<int64_t, double>(open_subjects, scale_, error_code_), PathType::Subject, true);
} }
void AddClip(const PathsD& clips) void AddClip(const PathsD& clips)
{ {
AddPaths(ScalePaths<int64_t, double>(clips, scale_), PathType::Clip, false); AddPaths(ScalePaths<int64_t, double>(clips, scale_, error_code_), PathType::Clip, false);
} }
bool Execute(ClipType clip_type, FillRule fill_rule, PathsD& closed_paths) bool Execute(ClipType clip_type, FillRule fill_rule, PathsD& closed_paths)
@@ -573,7 +620,7 @@ namespace Clipper2Lib {
if (ExecuteInternal(clip_type, fill_rule, true)) if (ExecuteInternal(clip_type, fill_rule, true))
{ {
polytree.Clear(); polytree.Clear();
polytree.SetInvScale(invScale_); polytree.SetScale(invScale_);
open_paths.clear(); open_paths.clear();
BuildTreeD(polytree, open_paths); BuildTreeD(polytree, open_paths);
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +1,18 @@
/******************************************************************************* /*******************************************************************************
* Author : Angus Johnson * * Author : Angus Johnson *
* Date : 15 October 2022 * * Date : 1 November 2023 *
* Website : http://www.angusj.com * * Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2022 * * Copyright : Angus Johnson 2010-2023 *
* Purpose : Minkowski Sum and Difference * * Purpose : Minkowski Sum and Difference *
* License : http://www.boost.org/LICENSE_1_0.txt * * License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/ *******************************************************************************/
#ifndef CLIPPER_MINKOWSKI_H #ifndef CLIPPER_MINKOWSKI_H
#define CLIPPER_MINKOWSKI_H #define CLIPPER_MINKOWSKI_H
#include <cstdlib> #include "clipper2/clipper.core.h"
#include <vector>
#include <string>
#include "clipper.core.h"
namespace Clipper2Lib namespace Clipper2Lib
{ {
namespace detail namespace detail
@@ -35,7 +32,7 @@ namespace Clipper2Lib
Path64 path2(pattern.size()); Path64 path2(pattern.size());
std::transform(pattern.cbegin(), pattern.cend(), std::transform(pattern.cbegin(), pattern.cend(),
path2.begin(), [p](const Point64& pt2) {return p + pt2; }); path2.begin(), [p](const Point64& pt2) {return p + pt2; });
tmp.push_back(path2); tmp.emplace_back(std::move(path2));
} }
} }
else else
@@ -45,7 +42,7 @@ namespace Clipper2Lib
Path64 path2(pattern.size()); Path64 path2(pattern.size());
std::transform(pattern.cbegin(), pattern.cend(), std::transform(pattern.cbegin(), pattern.cend(),
path2.begin(), [p](const Point64& pt2) {return p - pt2; }); path2.begin(), [p](const Point64& pt2) {return p - pt2; });
tmp.push_back(path2); tmp.emplace_back(std::move(path2));
} }
} }
@@ -59,14 +56,14 @@ namespace Clipper2Lib
Path64 quad; Path64 quad;
quad.reserve(4); quad.reserve(4);
{ {
quad.push_back(tmp[g][h]); quad.emplace_back(tmp[g][h]);
quad.push_back(tmp[i][h]); quad.emplace_back(tmp[i][h]);
quad.push_back(tmp[i][j]); quad.emplace_back(tmp[i][j]);
quad.push_back(tmp[g][j]); quad.emplace_back(tmp[g][j]);
}; };
if (!IsPositive(quad)) if (!IsPositive(quad))
std::reverse(quad.begin(), quad.end()); std::reverse(quad.begin(), quad.end());
result.push_back(quad); result.emplace_back(std::move(quad));
h = j; h = j;
} }
g = i; g = i;
@@ -92,11 +89,12 @@ namespace Clipper2Lib
inline PathsD MinkowskiSum(const PathD& pattern, const PathD& path, bool isClosed, int decimalPlaces = 2) inline PathsD MinkowskiSum(const PathD& pattern, const PathD& path, bool isClosed, int decimalPlaces = 2)
{ {
int error_code = 0;
double scale = pow(10, decimalPlaces); double scale = pow(10, decimalPlaces);
Path64 pat64 = ScalePath<int64_t, double>(pattern, scale); Path64 pat64 = ScalePath<int64_t, double>(pattern, scale, error_code);
Path64 path64 = ScalePath<int64_t, double>(path, scale); Path64 path64 = ScalePath<int64_t, double>(path, scale, error_code);
Paths64 tmp = detail::Union(detail::Minkowski(pat64, path64, true, isClosed), FillRule::NonZero); Paths64 tmp = detail::Union(detail::Minkowski(pat64, path64, true, isClosed), FillRule::NonZero);
return ScalePaths<double, int64_t>(tmp, 1 / scale); return ScalePaths<double, int64_t>(tmp, 1 / scale, error_code);
} }
inline Paths64 MinkowskiDiff(const Path64& pattern, const Path64& path, bool isClosed) inline Paths64 MinkowskiDiff(const Path64& pattern, const Path64& path, bool isClosed)
@@ -106,11 +104,12 @@ namespace Clipper2Lib
inline PathsD MinkowskiDiff(const PathD& pattern, const PathD& path, bool isClosed, int decimalPlaces = 2) inline PathsD MinkowskiDiff(const PathD& pattern, const PathD& path, bool isClosed, int decimalPlaces = 2)
{ {
int error_code = 0;
double scale = pow(10, decimalPlaces); double scale = pow(10, decimalPlaces);
Path64 pat64 = ScalePath<int64_t, double>(pattern, scale); Path64 pat64 = ScalePath<int64_t, double>(pattern, scale, error_code);
Path64 path64 = ScalePath<int64_t, double>(path, scale); Path64 path64 = ScalePath<int64_t, double>(path, scale, error_code);
Paths64 tmp = detail::Union(detail::Minkowski(pat64, path64, false, isClosed), FillRule::NonZero); Paths64 tmp = detail::Union(detail::Minkowski(pat64, path64, false, isClosed), FillRule::NonZero);
return ScalePaths<double, int64_t>(tmp, 1 / scale); return ScalePaths<double, int64_t>(tmp, 1 / scale, error_code);
} }
} // Clipper2Lib namespace } // Clipper2Lib namespace

View File

@@ -1,20 +1,24 @@
/******************************************************************************* /*******************************************************************************
* Author : Angus Johnson * * Author : Angus Johnson *
* Date : 15 October 2022 * * Date : 22 January 2025 *
* Website : http://www.angusj.com * * Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2022 * * Copyright : Angus Johnson 2010-2025 *
* Purpose : Path Offset (Inflate/Shrink) * * Purpose : Path Offset (Inflate/Shrink) *
* License : http://www.boost.org/LICENSE_1_0.txt * * License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/ *******************************************************************************/
#ifndef CLIPPER_OFFSET_H_ #ifndef CLIPPER_OFFSET_H_
#define CLIPPER_OFFSET_H_ #define CLIPPER_OFFSET_H_
#include "clipper.core.h" #include "clipper.core.h"
#include "clipper.engine.h"
#include <optional>
namespace Clipper2Lib { namespace Clipper2Lib {
enum class JoinType { Square, Round, Miter }; enum class JoinType { Square, Bevel, Round, Miter };
//Square : Joins are 'squared' at exactly the offset distance (more complex code)
//Bevel : Similar to Square, but the offset distance varies with angle (simple code & faster)
enum class EndType {Polygon, Joined, Butt, Square, Round}; enum class EndType {Polygon, Joined, Butt, Square, Round};
//Butt : offsets both sides of a path, with square blunt ends //Butt : offsets both sides of a path, with square blunt ends
@@ -23,49 +27,64 @@ enum class EndType {Polygon, Joined, Butt, Square, Round};
//Joined : offsets both sides of a path, with joined ends //Joined : offsets both sides of a path, with joined ends
//Polygon: offsets only one side of a closed path //Polygon: offsets only one side of a closed path
typedef std::function<double(const Path64& path, const PathD& path_normals, size_t curr_idx, size_t prev_idx)> DeltaCallback64;
class ClipperOffset { class ClipperOffset {
private: private:
class Group { class Group {
public: public:
Paths64 paths_in_; Paths64 paths_in;
Paths64 paths_out_; std::optional<size_t> lowest_path_idx{};
Path64 path_; bool is_reversed = false;
bool is_reversed_ = false; JoinType join_type;
JoinType join_type_; EndType end_type;
EndType end_type_; Group(const Paths64& _paths, JoinType _join_type, EndType _end_type);
Group(const Paths64& paths, JoinType join_type, EndType end_type) :
paths_in_(paths), join_type_(join_type), end_type_(end_type) {}
}; };
int error_code_ = 0;
double delta_ = 0.0;
double group_delta_ = 0.0; double group_delta_ = 0.0;
double abs_group_delta_ = 0.0;
double temp_lim_ = 0.0; double temp_lim_ = 0.0;
double steps_per_rad_ = 0.0; double steps_per_rad_ = 0.0;
double step_sin_ = 0.0;
double step_cos_ = 0.0;
PathD norms; PathD norms;
Paths64 solution; Path64 path_out;
Paths64* solution = nullptr;
PolyTree64* solution_tree = nullptr;
std::vector<Group> groups_; std::vector<Group> groups_;
JoinType join_type_ = JoinType::Square; JoinType join_type_ = JoinType::Bevel;
EndType end_type_ = EndType::Polygon;
double miter_limit_ = 0.0; double miter_limit_ = 0.0;
double arc_tolerance_ = 0.0; double arc_tolerance_ = 0.0;
bool merge_groups_ = true;
bool preserve_collinear_ = false; bool preserve_collinear_ = false;
bool reverse_solution_ = false; bool reverse_solution_ = false;
void DoSquare(Group& group, const Path64& path, size_t j, size_t k); #ifdef USINGZ
void DoMiter(Group& group, const Path64& path, size_t j, size_t k, double cos_a); ZCallback64 zCallback64_ = nullptr;
void DoRound(Group& group, const Path64& path, size_t j, size_t k, double angle); void ZCB(const Point64& bot1, const Point64& top1,
const Point64& bot2, const Point64& top2, Point64& ip);
#endif
DeltaCallback64 deltaCallback64_ = nullptr;
size_t CalcSolutionCapacity();
bool CheckReverseOrientation();
void DoBevel(const Path64& path, size_t j, size_t k);
void DoSquare(const Path64& path, size_t j, size_t k);
void DoMiter(const Path64& path, size_t j, size_t k, double cos_a);
void DoRound(const Path64& path, size_t j, size_t k, double angle);
void BuildNormals(const Path64& path); void BuildNormals(const Path64& path);
void OffsetPolygon(Group& group, Path64& path); void OffsetPolygon(Group& group, const Path64& path);
void OffsetOpenJoined(Group& group, Path64& path); void OffsetOpenJoined(Group& group, const Path64& path);
void OffsetOpenPath(Group& group, Path64& path, EndType endType); void OffsetOpenPath(Group& group, const Path64& path);
void OffsetPoint(Group& group, Path64& path, size_t j, size_t& k); void OffsetPoint(Group& group, const Path64& path, size_t j, size_t k);
void DoGroupOffset(Group &group, double delta); void DoGroupOffset(Group &group);
void ExecuteInternal(double delta);
public: public:
ClipperOffset(double miter_limit = 2.0, explicit ClipperOffset(double miter_limit = 2.0,
double arc_tolerance = 0.0, double arc_tolerance = 0.0,
bool preserve_collinear = false, bool preserve_collinear = false,
bool reverse_solution = false) : bool reverse_solution = false) :
miter_limit_(miter_limit), arc_tolerance_(arc_tolerance), miter_limit_(miter_limit), arc_tolerance_(arc_tolerance),
preserve_collinear_(preserve_collinear), preserve_collinear_(preserve_collinear),
@@ -73,13 +92,14 @@ public:
~ClipperOffset() { Clear(); }; ~ClipperOffset() { Clear(); };
int ErrorCode() const { return error_code_; };
void AddPath(const Path64& path, JoinType jt_, EndType et_); void AddPath(const Path64& path, JoinType jt_, EndType et_);
void AddPaths(const Paths64& paths, JoinType jt_, EndType et_); void AddPaths(const Paths64& paths, JoinType jt_, EndType et_);
void AddPath(const PathD &p, JoinType jt_, EndType et_);
void AddPaths(const PathsD &p, JoinType jt_, EndType et_);
void Clear() { groups_.clear(); norms.clear(); }; void Clear() { groups_.clear(); norms.clear(); };
Paths64 Execute(double delta); void Execute(double delta, Paths64& sols_64);
void Execute(double delta, PolyTree64& polytree);
void Execute(DeltaCallback64 delta_cb, Paths64& paths);
double MiterLimit() const { return miter_limit_; } double MiterLimit() const { return miter_limit_; }
void MiterLimit(double miter_limit) { miter_limit_ = miter_limit; } void MiterLimit(double miter_limit) { miter_limit_ = miter_limit; }
@@ -88,19 +108,17 @@ public:
double ArcTolerance() const { return arc_tolerance_; } double ArcTolerance() const { return arc_tolerance_; }
void ArcTolerance(double arc_tolerance) { arc_tolerance_ = arc_tolerance; } void ArcTolerance(double arc_tolerance) { arc_tolerance_ = arc_tolerance; }
//MergeGroups: A path group is one or more paths added via the AddPath or
//AddPaths methods. By default these path groups will be offset
//independently of other groups and this may cause overlaps (intersections).
//However, when MergeGroups is enabled, any overlapping offsets will be
//merged (via a clipping union operation) to remove overlaps.
bool MergeGroups() const { return merge_groups_; }
void MergeGroups(bool merge_groups) { merge_groups_ = merge_groups; }
bool PreserveCollinear() const { return preserve_collinear_; } bool PreserveCollinear() const { return preserve_collinear_; }
void PreserveCollinear(bool preserve_collinear){preserve_collinear_ = preserve_collinear;} void PreserveCollinear(bool preserve_collinear){preserve_collinear_ = preserve_collinear;}
bool ReverseSolution() const { return reverse_solution_; } bool ReverseSolution() const { return reverse_solution_; }
void ReverseSolution(bool reverse_solution) {reverse_solution_ = reverse_solution;} void ReverseSolution(bool reverse_solution) {reverse_solution_ = reverse_solution;}
#ifdef USINGZ
void SetZCallback(ZCallback64 cb) { zCallback64_ = cb; }
#endif
void SetDeltaCallback(DeltaCallback64 cb) { deltaCallback64_ = cb; }
}; };
} }

View File

@@ -1,49 +1,79 @@
/******************************************************************************* /*******************************************************************************
* Author : Angus Johnson * * Author : Angus Johnson *
* Date : 26 October 2022 * * Date : 5 July 2024 *
* Website : http://www.angusj.com * * Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2022 * * Copyright : Angus Johnson 2010-2024 *
* Purpose : FAST rectangular clipping * * Purpose : FAST rectangular clipping *
* License : http://www.boost.org/LICENSE_1_0.txt * * License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/ *******************************************************************************/
#ifndef CLIPPER_RECTCLIP_H #ifndef CLIPPER_RECTCLIP_H
#define CLIPPER_RECTCLIP_H #define CLIPPER_RECTCLIP_H
#include <cstdlib> #include "clipper2/clipper.core.h"
#include <vector> #include <queue>
#include "clipper.h"
#include "clipper.core.h"
namespace Clipper2Lib namespace Clipper2Lib
{ {
// Location: the order is important here, see StartLocsIsClockwise()
enum class Location { Left, Top, Right, Bottom, Inside }; enum class Location { Left, Top, Right, Bottom, Inside };
class RectClip { class OutPt2;
typedef std::vector<OutPt2*> OutPt2List;
class OutPt2 {
public:
Point64 pt;
size_t owner_idx = 0;
OutPt2List* edge = nullptr;
OutPt2* next = nullptr;
OutPt2* prev = nullptr;
};
//------------------------------------------------------------------------------
// RectClip64
//------------------------------------------------------------------------------
class RectClip64 {
private:
void ExecuteInternal(const Path64& path);
Path64 GetPath(OutPt2*& op);
protected: protected:
const Rect64 rect_; const Rect64 rect_;
const Point64 mp_; const Path64 rect_as_path_;
const Path64 rectPath_; const Point64 rect_mp_;
Path64 result_; Rect64 path_bounds_;
std::deque<OutPt2> op_container_;
OutPt2List results_; // each path can be broken into multiples
OutPt2List edges_[8]; // clockwise and counter-clockwise
std::vector<Location> start_locs_; std::vector<Location> start_locs_;
void CheckEdges();
void TidyEdges(size_t idx, OutPt2List& cw, OutPt2List& ccw);
void GetNextLocation(const Path64& path, void GetNextLocation(const Path64& path,
Location& loc, int& i, int highI); Location& loc, size_t& i, size_t highI);
OutPt2* Add(Point64 pt, bool start_new = false);
void AddCorner(Location prev, Location curr); void AddCorner(Location prev, Location curr);
void AddCorner(Location& loc, bool isClockwise); void AddCorner(Location& loc, bool isClockwise);
public: public:
RectClip(const Rect64& rect) : explicit RectClip64(const Rect64& rect) :
rect_(rect), rect_(rect),
mp_(rect.MidPoint()), rect_as_path_(rect.AsPath()),
rectPath_(rect.AsPath()) {} rect_mp_(rect.MidPoint()) {}
Path64 Execute(const Path64& path); Paths64 Execute(const Paths64& paths);
}; };
class RectClipLines : public RectClip { //------------------------------------------------------------------------------
// RectClipLines64
//------------------------------------------------------------------------------
class RectClipLines64 : public RectClip64 {
private:
void ExecuteInternal(const Path64& path);
Path64 GetPath(OutPt2*& op);
public: public:
RectClipLines(const Rect64& rect) : RectClip(rect) {}; explicit RectClipLines64(const Rect64& rect) : RectClip64(rect) {};
Paths64 Execute(const Path64& path); Paths64 Execute(const Paths64& paths);
}; };
} // Clipper2Lib namespace } // Clipper2Lib namespace

View File

@@ -0,0 +1,6 @@
#ifndef CLIPPER_VERSION_H
#define CLIPPER_VERSION_H
constexpr auto CLIPPER2_VERSION = "1.5.2";
#endif // CLIPPER_VERSION_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,48 +1,72 @@
/******************************************************************************* /*******************************************************************************
* Author : Angus Johnson * * Author : Angus Johnson *
* Date : 15 October 2022 * * Date : 22 January 2025 *
* Website : http://www.angusj.com * * Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2022 * * Copyright : Angus Johnson 2010-2025 *
* Purpose : Path Offset (Inflate/Shrink) * * Purpose : Path Offset (Inflate/Shrink) *
* License : http://www.boost.org/LICENSE_1_0.txt * * License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/ *******************************************************************************/
#include <cmath>
#include "clipper2/clipper.h" #include "clipper2/clipper.h"
#include "clipper2/clipper.offset.h" #include "clipper2/clipper.offset.h"
namespace Clipper2Lib { namespace Clipper2Lib {
const double default_arc_tolerance = 0.25;
const double floating_point_tolerance = 1e-12; const double floating_point_tolerance = 1e-12;
// Clipper2 approximates arcs by using series of relatively short straight
//line segments. And logically, shorter line segments will produce better arc
// approximations. But very short segments can degrade performance, usually
// with little or no discernable improvement in curve quality. Very short
// segments can even detract from curve quality, due to the effects of integer
// rounding. Since there isn't an optimal number of line segments for any given
// arc radius (that perfectly balances curve approximation with performance),
// arc tolerance is user defined. Nevertheless, when the user doesn't define
// an arc tolerance (ie leaves alone the 0 default value), the calculated
// default arc tolerance (offset_radius / 500) generally produces good (smooth)
// arc approximations without producing excessively small segment lengths.
// See also: https://www.angusj.com/clipper2/Docs/Trigonometry.htm
const double arc_const = 0.002; // <-- 1/500
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Miscellaneous methods // Miscellaneous methods
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
Paths64::size_type GetLowestPolygonIdx(const Paths64& paths) std::optional<size_t> GetLowestClosedPathIdx(const Paths64& paths)
{ {
Paths64::size_type result = 0; std::optional<size_t> result;
Point64 lp = Point64(static_cast<int64_t>(0), Point64 botPt = Point64(INT64_MAX, INT64_MIN);
std::numeric_limits<int64_t>::min()); for (size_t i = 0; i < paths.size(); ++i)
{
for (Paths64::size_type i = 0 ; i < paths.size(); ++i) for (const Point64& pt : paths[i])
for (const Point64& p : paths[i]) {
{ if ((pt.y < botPt.y) ||
if (p.y < lp.y || (p.y == lp.y && p.x >= lp.x)) continue; ((pt.y == botPt.y) && (pt.x >= botPt.x))) continue;
result = i; result = i;
lp = p; botPt.x = pt.x;
} botPt.y = pt.y;
}
}
return result; return result;
} }
PointD GetUnitNormal(const Point64& pt1, const Point64& pt2) inline double Hypot(double x, double y)
{
// given that this is an internal function, and given the x and y parameters
// will always be coordinate values (or the difference between coordinate values),
// x and y should always be within INT64_MIN to INT64_MAX. Consequently,
// there should be no risk that the following computation will overflow
// see https://stackoverflow.com/a/32436148/359538
return std::sqrt(x * x + y * y);
}
static PointD GetUnitNormal(const Point64& pt1, const Point64& pt2)
{ {
double dx, dy, inverse_hypot;
if (pt1 == pt2) return PointD(0.0, 0.0); if (pt1 == pt2) return PointD(0.0, 0.0);
dx = static_cast<double>(pt2.x - pt1.x); double dx = static_cast<double>(pt2.x - pt1.x);
dy = static_cast<double>(pt2.y - pt1.y); double dy = static_cast<double>(pt2.y - pt1.y);
inverse_hypot = 1.0 / hypot(dx, dy); double inverse_hypot = 1.0 / Hypot(dx, dy);
dx *= inverse_hypot; dx *= inverse_hypot;
dy *= inverse_hypot; dy *= inverse_hypot;
return PointD(dy, -dx); return PointD(dy, -dx);
@@ -53,15 +77,8 @@ inline bool AlmostZero(double value, double epsilon = 0.001)
return std::fabs(value) < epsilon; return std::fabs(value) < epsilon;
} }
inline double Hypot(double x, double y)
{
//see https://stackoverflow.com/a/32436148/359538
return std::sqrt(x * x + y * y);
}
inline PointD NormalizeVector(const PointD& vec) inline PointD NormalizeVector(const PointD& vec)
{ {
double h = Hypot(vec.x, vec.y); double h = Hypot(vec.x, vec.y);
if (AlmostZero(h)) return PointD(0,0); if (AlmostZero(h)) return PointD(0,0);
double inverseHypot = 1 / h; double inverseHypot = 1 / h;
@@ -78,14 +95,63 @@ inline bool IsClosedPath(EndType et)
return et == EndType::Polygon || et == EndType::Joined; return et == EndType::Polygon || et == EndType::Joined;
} }
inline Point64 GetPerpendic(const Point64& pt, const PointD& norm, double delta) static inline Point64 GetPerpendic(const Point64& pt, const PointD& norm, double delta)
{ {
#ifdef USINGZ
return Point64(pt.x + norm.x * delta, pt.y + norm.y * delta, pt.z);
#else
return Point64(pt.x + norm.x * delta, pt.y + norm.y * delta); return Point64(pt.x + norm.x * delta, pt.y + norm.y * delta);
#endif
} }
inline PointD GetPerpendicD(const Point64& pt, const PointD& norm, double delta) inline PointD GetPerpendicD(const Point64& pt, const PointD& norm, double delta)
{ {
#ifdef USINGZ
return PointD(pt.x + norm.x * delta, pt.y + norm.y * delta, pt.z);
#else
return PointD(pt.x + norm.x * delta, pt.y + norm.y * delta); return PointD(pt.x + norm.x * delta, pt.y + norm.y * delta);
#endif
}
inline void NegatePath(PathD& path)
{
for (PointD& pt : path)
{
pt.x = -pt.x;
pt.y = -pt.y;
#ifdef USINGZ
pt.z = pt.z;
#endif
}
}
//------------------------------------------------------------------------------
// ClipperOffset::Group methods
//------------------------------------------------------------------------------
ClipperOffset::Group::Group(const Paths64& _paths, JoinType _join_type, EndType _end_type):
paths_in(_paths), join_type(_join_type), end_type(_end_type)
{
bool is_joined =
(end_type == EndType::Polygon) ||
(end_type == EndType::Joined);
for (Path64& p: paths_in)
StripDuplicates(p, is_joined);
if (end_type == EndType::Polygon)
{
lowest_path_idx = GetLowestClosedPathIdx(paths_in);
// the lowermost path must be an outer path, so if its orientation is negative,
// then flag the whole group is 'reversed' (will negate delta etc.)
// as this is much more efficient than reversing every path.
is_reversed = (lowest_path_idx.has_value()) && Area(paths_in[lowest_path_idx.value()]) < 0;
}
else
{
lowest_path_idx = std::nullopt;
is_reversed = false;
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -94,28 +160,13 @@ inline PointD GetPerpendicD(const Point64& pt, const PointD& norm, double delta)
void ClipperOffset::AddPath(const Path64& path, JoinType jt_, EndType et_) void ClipperOffset::AddPath(const Path64& path, JoinType jt_, EndType et_)
{ {
Paths64 paths; groups_.emplace_back(Paths64(1, path), jt_, et_);
paths.push_back(path);
AddPaths(paths, jt_, et_);
} }
void ClipperOffset::AddPaths(const Paths64 &paths, JoinType jt_, EndType et_) void ClipperOffset::AddPaths(const Paths64 &paths, JoinType jt_, EndType et_)
{ {
if (paths.size() == 0) return; if (paths.size() == 0) return;
groups_.push_back(Group(paths, jt_, et_)); groups_.emplace_back(paths, jt_, et_);
}
void ClipperOffset::AddPath(const Clipper2Lib::PathD& path, JoinType jt_, EndType et_)
{
PathsD paths;
paths.push_back(path);
AddPaths(paths, jt_, et_);
}
void ClipperOffset::AddPaths(const PathsD& paths, JoinType jt_, EndType et_)
{
if (paths.size() == 0) return;
groups_.push_back(Group(PathsDToPaths64(paths), jt_, et_));
} }
void ClipperOffset::BuildNormals(const Path64& path) void ClipperOffset::BuildNormals(const Path64& path)
@@ -123,64 +174,55 @@ void ClipperOffset::BuildNormals(const Path64& path)
norms.clear(); norms.clear();
norms.reserve(path.size()); norms.reserve(path.size());
if (path.size() == 0) return; if (path.size() == 0) return;
Path64::const_iterator path_iter, path_last_iter = --path.cend(); Path64::const_iterator path_iter, path_stop_iter = --path.cend();
for (path_iter = path.cbegin(); path_iter != path_last_iter; ++path_iter) for (path_iter = path.cbegin(); path_iter != path_stop_iter; ++path_iter)
norms.push_back(GetUnitNormal(*path_iter,*(path_iter +1))); norms.emplace_back(GetUnitNormal(*path_iter,*(path_iter +1)));
norms.push_back(GetUnitNormal(*path_last_iter, *(path.cbegin()))); norms.emplace_back(GetUnitNormal(*path_stop_iter, *(path.cbegin())));
} }
inline PointD TranslatePoint(const PointD& pt, double dx, double dy) void ClipperOffset::DoBevel(const Path64& path, size_t j, size_t k)
{ {
return PointD(pt.x + dx, pt.y + dy); PointD pt1, pt2;
} if (j == k)
inline PointD ReflectPoint(const PointD& pt, const PointD& pivot)
{
return PointD(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y));
}
PointD IntersectPoint(const PointD& pt1a, const PointD& pt1b,
const PointD& pt2a, const PointD& pt2b)
{
if (pt1a.x == pt1b.x) //vertical
{ {
if (pt2a.x == pt2b.x) return PointD(0, 0); double abs_delta = std::abs(group_delta_);
#ifdef USINGZ
double m2 = (pt2b.y - pt2a.y) / (pt2b.x - pt2a.x); pt1 = PointD(path[j].x - abs_delta * norms[j].x, path[j].y - abs_delta * norms[j].y, path[j].z);
double b2 = pt2a.y - m2 * pt2a.x; pt2 = PointD(path[j].x + abs_delta * norms[j].x, path[j].y + abs_delta * norms[j].y, path[j].z);
return PointD(pt1a.x, m2 * pt1a.x + b2); #else
} pt1 = PointD(path[j].x - abs_delta * norms[j].x, path[j].y - abs_delta * norms[j].y);
else if (pt2a.x == pt2b.x) //vertical pt2 = PointD(path[j].x + abs_delta * norms[j].x, path[j].y + abs_delta * norms[j].y);
{ #endif
double m1 = (pt1b.y - pt1a.y) / (pt1b.x - pt1a.x);
double b1 = pt1a.y - m1 * pt1a.x;
return PointD(pt2a.x, m1 * pt2a.x + b1);
} }
else else
{ {
double m1 = (pt1b.y - pt1a.y) / (pt1b.x - pt1a.x); #ifdef USINGZ
double b1 = pt1a.y - m1 * pt1a.x; pt1 = PointD(path[j].x + group_delta_ * norms[k].x, path[j].y + group_delta_ * norms[k].y, path[j].z);
double m2 = (pt2b.y - pt2a.y) / (pt2b.x - pt2a.x); pt2 = PointD(path[j].x + group_delta_ * norms[j].x, path[j].y + group_delta_ * norms[j].y, path[j].z);
double b2 = pt2a.y - m2 * pt2a.x; #else
if (m1 == m2) return PointD(0, 0); pt1 = PointD(path[j].x + group_delta_ * norms[k].x, path[j].y + group_delta_ * norms[k].y);
double x = (b2 - b1) / (m1 - m2); pt2 = PointD(path[j].x + group_delta_ * norms[j].x, path[j].y + group_delta_ * norms[j].y);
return PointD(x, m1 * x + b1); #endif
} }
path_out.emplace_back(pt1);
path_out.emplace_back(pt2);
} }
void ClipperOffset::DoSquare(Group& group, const Path64& path, size_t j, size_t k) void ClipperOffset::DoSquare(const Path64& path, size_t j, size_t k)
{ {
PointD vec; PointD vec;
if (j == k) if (j == k)
vec = PointD(norms[0].y, -norms[0].x); vec = PointD(norms[j].y, -norms[j].x);
else else
vec = GetAvgUnitVector( vec = GetAvgUnitVector(
PointD(-norms[k].y, norms[k].x), PointD(-norms[k].y, norms[k].x),
PointD(norms[j].y, -norms[j].x)); PointD(norms[j].y, -norms[j].x));
double abs_delta = std::abs(group_delta_);
// now offset the original vertex delta units along unit vector // now offset the original vertex delta units along unit vector
PointD ptQ = PointD(path[j]); PointD ptQ = PointD(path[j]);
ptQ = TranslatePoint(ptQ, abs_group_delta_ * vec.x, abs_group_delta_ * vec.y); ptQ = TranslatePoint(ptQ, abs_delta * vec.x, abs_delta * vec.y);
// get perpendicular vertices // get perpendicular vertices
PointD pt1 = TranslatePoint(ptQ, group_delta_ * vec.y, group_delta_ * -vec.x); PointD pt1 = TranslatePoint(ptQ, group_delta_ * vec.y, group_delta_ * -vec.x);
PointD pt2 = TranslatePoint(ptQ, group_delta_ * -vec.y, group_delta_ * vec.x); PointD pt2 = TranslatePoint(ptQ, group_delta_ * -vec.y, group_delta_ * vec.x);
@@ -189,51 +231,77 @@ void ClipperOffset::DoSquare(Group& group, const Path64& path, size_t j, size_t
if (j == k) if (j == k)
{ {
PointD pt4 = PointD(pt3.x + vec.x * group_delta_, pt3.y + vec.y * group_delta_); PointD pt4 = PointD(pt3.x + vec.x * group_delta_, pt3.y + vec.y * group_delta_);
PointD pt = IntersectPoint(pt1, pt2, pt3, pt4); PointD pt = ptQ;
GetSegmentIntersectPt(pt1, pt2, pt3, pt4, pt);
//get the second intersect point through reflecion //get the second intersect point through reflecion
group.path_.push_back(Point64(ReflectPoint(pt, ptQ))); path_out.emplace_back(ReflectPoint(pt, ptQ));
group.path_.push_back(Point64(pt)); path_out.emplace_back(pt);
} }
else else
{ {
PointD pt4 = GetPerpendicD(path[j], norms[k], group_delta_); PointD pt4 = GetPerpendicD(path[j], norms[k], group_delta_);
PointD pt = IntersectPoint(pt1, pt2, pt3, pt4); PointD pt = ptQ;
group.path_.push_back(Point64(pt)); GetSegmentIntersectPt(pt1, pt2, pt3, pt4, pt);
path_out.emplace_back(pt);
//get the second intersect point through reflecion //get the second intersect point through reflecion
group.path_.push_back(Point64(ReflectPoint(pt, ptQ))); path_out.emplace_back(ReflectPoint(pt, ptQ));
} }
} }
void ClipperOffset::DoMiter(Group& group, const Path64& path, size_t j, size_t k, double cos_a) void ClipperOffset::DoMiter(const Path64& path, size_t j, size_t k, double cos_a)
{ {
double q = group_delta_ / (cos_a + 1); double q = group_delta_ / (cos_a + 1);
group.path_.push_back(Point64( #ifdef USINGZ
path_out.emplace_back(
path[j].x + (norms[k].x + norms[j].x) * q, path[j].x + (norms[k].x + norms[j].x) * q,
path[j].y + (norms[k].y + norms[j].y) * q)); path[j].y + (norms[k].y + norms[j].y) * q,
path[j].z);
#else
path_out.emplace_back(
path[j].x + (norms[k].x + norms[j].x) * q,
path[j].y + (norms[k].y + norms[j].y) * q);
#endif
} }
void ClipperOffset::DoRound(Group& group, const Path64& path, size_t j, size_t k, double angle) void ClipperOffset::DoRound(const Path64& path, size_t j, size_t k, double angle)
{ {
//even though angle may be negative this is a convex join if (deltaCallback64_) {
Point64 pt = path[j]; // when deltaCallback64_ is assigned, group_delta_ won't be constant,
int steps = static_cast<int>(std::ceil(steps_per_rad_ * std::abs(angle))); // so we'll need to do the following calculations for *every* vertex.
double step_sin = std::sin(angle / steps); double abs_delta = std::fabs(group_delta_);
double step_cos = std::cos(angle / steps); double arcTol = (arc_tolerance_ > floating_point_tolerance ?
std::min(abs_delta, arc_tolerance_) : abs_delta * arc_const);
PointD pt2 = PointD(norms[k].x * group_delta_, norms[k].y * group_delta_); double steps_per_360 = std::min(PI / std::acos(1 - arcTol / abs_delta), abs_delta * PI);
if (j == k) pt2.Negate(); step_sin_ = std::sin(2 * PI / steps_per_360);
step_cos_ = std::cos(2 * PI / steps_per_360);
group.path_.push_back(Point64(pt.x + pt2.x, pt.y + pt2.y)); if (group_delta_ < 0.0) step_sin_ = -step_sin_;
for (int i = 0; i < steps; i++) steps_per_rad_ = steps_per_360 / (2 * PI);
{
pt2 = PointD(pt2.x * step_cos - step_sin * pt2.y,
pt2.x * step_sin + pt2.y * step_cos);
group.path_.push_back(Point64(pt.x + pt2.x, pt.y + pt2.y));
} }
group.path_.push_back(GetPerpendic(path[j], norms[j], group_delta_));
Point64 pt = path[j];
PointD offsetVec = PointD(norms[k].x * group_delta_, norms[k].y * group_delta_);
if (j == k) offsetVec.Negate();
#ifdef USINGZ
path_out.emplace_back(pt.x + offsetVec.x, pt.y + offsetVec.y, pt.z);
#else
path_out.emplace_back(pt.x + offsetVec.x, pt.y + offsetVec.y);
#endif
int steps = static_cast<int>(std::ceil(steps_per_rad_ * std::abs(angle))); // #448, #456
for (int i = 1; i < steps; ++i) // ie 1 less than steps
{
offsetVec = PointD(offsetVec.x * step_cos_ - step_sin_ * offsetVec.y,
offsetVec.x * step_sin_ + offsetVec.y * step_cos_);
#ifdef USINGZ
path_out.emplace_back(pt.x + offsetVec.x, pt.y + offsetVec.y, pt.z);
#else
path_out.emplace_back(pt.x + offsetVec.x, pt.y + offsetVec.y);
#endif
}
path_out.emplace_back(GetPerpendic(path[j], norms[j], group_delta_));
} }
void ClipperOffset::OffsetPoint(Group& group, Path64& path, size_t j, size_t& k) void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size_t k)
{ {
// Let A = change in angle where edges join // Let A = change in angle where edges join
// A == 0: ie no change in angle (flat join) // A == 0: ie no change in angle (flat join)
@@ -241,245 +309,346 @@ void ClipperOffset::OffsetPoint(Group& group, Path64& path, size_t j, size_t& k)
// sin(A) < 0: right turning // sin(A) < 0: right turning
// cos(A) < 0: change in angle is more than 90 degree // cos(A) < 0: change in angle is more than 90 degree
if (path[j] == path[k]) { k = j; return; } if (path[j] == path[k]) return;
double sin_a = CrossProduct(norms[j], norms[k]); double sin_a = CrossProduct(norms[j], norms[k]);
double cos_a = DotProduct(norms[j], norms[k]); double cos_a = DotProduct(norms[j], norms[k]);
if (sin_a > 1.0) sin_a = 1.0; if (sin_a > 1.0) sin_a = 1.0;
else if (sin_a < -1.0) sin_a = -1.0; else if (sin_a < -1.0) sin_a = -1.0;
bool almostNoAngle = AlmostZero(sin_a) && cos_a > 0; if (deltaCallback64_) {
// when there's almost no angle of deviation or it's concave group_delta_ = deltaCallback64_(path, norms, j, k);
if (almostNoAngle || (sin_a * group_delta_ < 0)) if (group.is_reversed) group_delta_ = -group_delta_;
{
Point64 p1 = Point64(
path[j].x + norms[k].x * group_delta_,
path[j].y + norms[k].y * group_delta_);
Point64 p2 = Point64(
path[j].x + norms[j].x * group_delta_,
path[j].y + norms[j].y * group_delta_);
group.path_.push_back(p1);
if (p1 != p2)
{
// when concave add an extra vertex to ensure neat clipping
if (!almostNoAngle) group.path_.push_back(path[j]);
group.path_.push_back(p2);
}
} }
else // it's convex if (std::fabs(group_delta_) <= floating_point_tolerance)
{ {
if (join_type_ == JoinType::Round) path_out.emplace_back(path[j]);
DoRound(group, path, j, k, std::atan2(sin_a, cos_a)); return;
else if (join_type_ == JoinType::Miter)
{
// miter unless the angle is so acute the miter would exceeds ML
if (cos_a > temp_lim_ - 1) DoMiter(group, path, j, k, cos_a);
else DoSquare(group, path, j, k);
}
// don't bother squaring angles that deviate < ~20 degrees because
// squaring will be indistinguishable from mitering and just be a lot slower
else if (cos_a > 0.9)
DoMiter(group, path, j, k, cos_a);
else
DoSquare(group, path, j, k);
} }
k = j;
if (cos_a > -0.999 && (sin_a * group_delta_ < 0)) // test for concavity first (#593)
{
// is concave
// by far the simplest way to construct concave joins, especially those joining very
// short segments, is to insert 3 points that produce negative regions. These regions
// will be removed later by the finishing union operation. This is also the best way
// to ensure that path reversals (ie over-shrunk paths) are removed.
#ifdef USINGZ
path_out.emplace_back(GetPerpendic(path[j], norms[k], group_delta_), path[j].z);
path_out.emplace_back(path[j]); // (#405, #873, #916)
path_out.emplace_back(GetPerpendic(path[j], norms[j], group_delta_), path[j].z);
#else
path_out.emplace_back(GetPerpendic(path[j], norms[k], group_delta_));
path_out.emplace_back(path[j]); // (#405, #873, #916)
path_out.emplace_back(GetPerpendic(path[j], norms[j], group_delta_));
#endif
}
else if (cos_a > 0.999 && join_type_ != JoinType::Round)
{
// almost straight - less than 2.5 degree (#424, #482, #526 & #724)
DoMiter(path, j, k, cos_a);
}
else if (join_type_ == JoinType::Miter)
{
// miter unless the angle is sufficiently acute to exceed ML
if (cos_a > temp_lim_ - 1) DoMiter(path, j, k, cos_a);
else DoSquare(path, j, k);
}
else if (join_type_ == JoinType::Round)
DoRound(path, j, k, std::atan2(sin_a, cos_a));
else if ( join_type_ == JoinType::Bevel)
DoBevel(path, j, k);
else
DoSquare(path, j, k);
} }
void ClipperOffset::OffsetPolygon(Group& group, Path64& path) void ClipperOffset::OffsetPolygon(Group& group, const Path64& path)
{ {
group.path_.clear(); path_out.clear();
for (Path64::size_type i = 0, j = path.size() -1; i < path.size(); j = i, ++i) for (Path64::size_type j = 0, k = path.size() - 1; j < path.size(); k = j, ++j)
OffsetPoint(group, path, i, j); OffsetPoint(group, path, j, k);
group.paths_out_.push_back(group.path_); solution->emplace_back(path_out);
} }
void ClipperOffset::OffsetOpenJoined(Group& group, Path64& path) void ClipperOffset::OffsetOpenJoined(Group& group, const Path64& path)
{ {
OffsetPolygon(group, path); OffsetPolygon(group, path);
std::reverse(path.begin(), path.end()); Path64 reverse_path(path);
BuildNormals(path); std::reverse(reverse_path.begin(), reverse_path.end());
OffsetPolygon(group, path);
//rebuild normals
std::reverse(norms.begin(), norms.end());
norms.emplace_back(norms[0]);
norms.erase(norms.begin());
NegatePath(norms);
OffsetPolygon(group, reverse_path);
} }
void ClipperOffset::OffsetOpenPath(Group& group, Path64& path, EndType end_type) void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path)
{ {
group.path_.clear();
// do the line start cap // do the line start cap
switch (end_type) if (deltaCallback64_) group_delta_ = deltaCallback64_(path, norms, 0, 0);
if (std::fabs(group_delta_) <= floating_point_tolerance)
path_out.emplace_back(path[0]);
else
{ {
case EndType::Butt: switch (end_type_)
group.path_.push_back(Point64( {
path[0].x - norms[0].x * group_delta_, case EndType::Butt:
path[0].y - norms[0].y * group_delta_)); DoBevel(path, 0, 0);
group.path_.push_back(GetPerpendic(path[0], norms[0], group_delta_)); break;
break; case EndType::Round:
case EndType::Round: DoRound(path, 0, 0, PI);
DoRound(group, path, 0,0, PI); break;
break; default:
default: DoSquare(path, 0, 0);
DoSquare(group, path, 0, 0); break;
break; }
} }
size_t highI = path.size() - 1; size_t highI = path.size() - 1;
// offset the left side going forward // offset the left side going forward
for (Path64::size_type i = 1, k = 0; i < highI; ++i) for (Path64::size_type j = 1, k = 0; j < highI; k = j, ++j)
OffsetPoint(group, path, i, k); OffsetPoint(group, path, j, k);
// reverse normals // reverse normals
for (size_t i = highI; i > 0; --i) for (size_t i = highI; i > 0; --i)
norms[i] = PointD(-norms[i - 1].x, -norms[i - 1].y); norms[i] = PointD(-norms[i - 1].x, -norms[i - 1].y);
norms[0] = norms[highI]; norms[0] = norms[highI];
// do the line end cap // do the line end cap
switch (end_type) if (deltaCallback64_)
group_delta_ = deltaCallback64_(path, norms, highI, highI);
if (std::fabs(group_delta_) <= floating_point_tolerance)
path_out.emplace_back(path[highI]);
else
{ {
case EndType::Butt: switch (end_type_)
group.path_.push_back(Point64( {
path[highI].x - norms[highI].x * group_delta_, case EndType::Butt:
path[highI].y - norms[highI].y * group_delta_)); DoBevel(path, highI, highI);
group.path_.push_back(GetPerpendic(path[highI], norms[highI], group_delta_)); break;
break; case EndType::Round:
case EndType::Round: DoRound(path, highI, highI, PI);
DoRound(group, path, highI, highI, PI); break;
break; default:
default: DoSquare(path, highI, highI);
DoSquare(group, path, highI, highI); break;
break; }
} }
for (size_t i = highI, k = 0; i > 0; --i) for (size_t j = highI -1, k = highI; j > 0; k = j, --j)
OffsetPoint(group, path, i, k); OffsetPoint(group, path, j, k);
group.paths_out_.push_back(group.path_); solution->emplace_back(path_out);
} }
void ClipperOffset::DoGroupOffset(Group& group, double delta) void ClipperOffset::DoGroupOffset(Group& group)
{ {
if (group.end_type_ != EndType::Polygon) delta = std::abs(delta) * 0.5; if (group.end_type == EndType::Polygon)
bool isClosedPaths = IsClosedPath(group.end_type_);
if (isClosedPaths)
{ {
//the lowermost polygon must be an outer polygon. So we can use that as the // a straight path (2 points) can now also be 'polygon' offset
//designated orientation for outer polygons (needed for tidy-up clipping) // where the ends will be treated as (180 deg.) joins
Paths64::size_type lowestIdx = GetLowestPolygonIdx(group.paths_in_); if (!group.lowest_path_idx.has_value()) delta_ = std::abs(delta_);
// nb: don't use the default orientation here ... group_delta_ = (group.is_reversed) ? -delta_ : delta_;
double area = Area(group.paths_in_[lowestIdx]); }
if (area == 0) return;
group.is_reversed_ = (area < 0);
if (group.is_reversed_) delta = -delta;
}
else else
group.is_reversed_ = false; group_delta_ = std::abs(delta_);// *0.5;
group_delta_ = delta; double abs_delta = std::fabs(group_delta_);
abs_group_delta_ = std::abs(group_delta_); join_type_ = group.join_type;
join_type_ = group.join_type_; end_type_ = group.end_type;
double arcTol = (arc_tolerance_ > floating_point_tolerance ? arc_tolerance_ if (group.join_type == JoinType::Round || group.end_type == EndType::Round)
: std::log10(2 + abs_group_delta_) * default_arc_tolerance); // empirically derived
//calculate a sensible number of steps (for 360 deg for the given offset
if (group.join_type_ == JoinType::Round || group.end_type_ == EndType::Round)
{ {
steps_per_rad_ = PI / std::acos(1 - arcTol / abs_group_delta_) / (PI *2); // calculate the number of steps required to approximate a circle
// (see https://www.angusj.com/clipper2/Docs/Trigonometry.htm)
// arcTol - when arc_tolerance_ is undefined (0) then curve imprecision
// will be relative to the size of the offset (delta). Obviously very
//large offsets will almost always require much less precision.
double arcTol = (arc_tolerance_ > floating_point_tolerance) ?
std::min(abs_delta, arc_tolerance_) : abs_delta * arc_const;
double steps_per_360 = std::min(PI / std::acos(1 - arcTol / abs_delta), abs_delta * PI);
step_sin_ = std::sin(2 * PI / steps_per_360);
step_cos_ = std::cos(2 * PI / steps_per_360);
if (group_delta_ < 0.0) step_sin_ = -step_sin_;
steps_per_rad_ = steps_per_360 / (2 * PI);
} }
bool is_closed_path = IsClosedPath(group.end_type_); //double min_area = PI * Sqr(group_delta_);
Paths64::const_iterator path_iter; Paths64::const_iterator path_in_it = group.paths_in.cbegin();
for(path_iter = group.paths_in_.cbegin(); path_iter != group.paths_in_.cend(); ++path_iter) for ( ; path_in_it != group.paths_in.cend(); ++path_in_it)
{ {
Path64 path = StripDuplicates(*path_iter, is_closed_path); Path64::size_type pathLen = path_in_it->size();
Path64::size_type cnt = path.size(); path_out.clear();
if (cnt == 0) continue;
if (cnt == 1) // single point - only valid with open paths if (pathLen == 1) // single point
{ {
group.path_ = Path64(); if (deltaCallback64_)
//single vertex so build a circle or square ...
if (group.join_type_ == JoinType::Round)
{ {
double radius = abs_group_delta_; group_delta_ = deltaCallback64_(*path_in_it, norms, 0, 0);
group.path_ = Ellipse(path[0], radius, radius); if (group.is_reversed) group_delta_ = -group_delta_;
abs_delta = std::fabs(group_delta_);
}
if (group_delta_ < 1) continue;
const Point64& pt = (*path_in_it)[0];
//single vertex so build a circle or square ...
if (group.join_type == JoinType::Round)
{
double radius = abs_delta;
size_t steps = steps_per_rad_ > 0 ? static_cast<size_t>(std::ceil(steps_per_rad_ * 2 * PI)) : 0; //#617
path_out = Ellipse(pt, radius, radius, steps);
#ifdef USINGZ
for (auto& p : path_out) p.z = pt.z;
#endif
} }
else else
{ {
int d = (int)std::ceil(abs_group_delta_); int d = (int)std::ceil(abs_delta);
Rect64 r = Rect64(path[0].x - d, path[0].y - d, path[0].x + d, path[0].y + d); Rect64 r = Rect64(pt.x - d, pt.y - d, pt.x + d, pt.y + d);
group.path_ = r.AsPath(); path_out = r.AsPath();
#ifdef USINGZ
for (auto& p : path_out) p.z = pt.z;
#endif
} }
group.paths_out_.push_back(group.path_);
}
else
{
BuildNormals(path);
if (group.end_type_ == EndType::Polygon) OffsetPolygon(group, path);
else if (group.end_type_ == EndType::Joined) OffsetOpenJoined(group, path);
else OffsetOpenPath(group, path, group.end_type_);
}
}
if (!merge_groups_) solution->emplace_back(path_out);
{ continue;
//clean up self-intersections ... } // end of offsetting a single point
Clipper64 c;
c.PreserveCollinear = false;
//the solution should retain the orientation of the input
c.ReverseSolution = reverse_solution_ != group.is_reversed_;
c.AddSubject(group.paths_out_);
if (group.is_reversed_)
c.Execute(ClipType::Union, FillRule::Negative, group.paths_out_);
else
c.Execute(ClipType::Union, FillRule::Positive, group.paths_out_);
}
solution.reserve(solution.size() + group.paths_out_.size()); if ((pathLen == 2) && (group.end_type == EndType::Joined))
copy(group.paths_out_.begin(), group.paths_out_.end(), back_inserter(solution)); end_type_ = (group.join_type == JoinType::Round) ?
group.paths_out_.clear(); EndType::Round :
EndType::Square;
BuildNormals(*path_in_it);
if (end_type_ == EndType::Polygon) OffsetPolygon(group, *path_in_it);
else if (end_type_ == EndType::Joined) OffsetOpenJoined(group, *path_in_it);
else OffsetOpenPath(group, *path_in_it);
}
} }
Paths64 ClipperOffset::Execute(double delta) #ifdef USINGZ
void ClipperOffset::ZCB(const Point64& bot1, const Point64& top1,
const Point64& bot2, const Point64& top2, Point64& ip)
{ {
solution.clear(); if (bot1.z && ((bot1.z == bot2.z) || (bot1.z == top2.z))) ip.z = bot1.z;
if (std::abs(delta) < default_arc_tolerance) else if (bot2.z && (bot2.z == top1.z)) ip.z = bot2.z;
{ else if (top1.z && (top1.z == top2.z)) ip.z = top1.z;
for (const Group& group : groups_) else if (zCallback64_) zCallback64_(bot1, top1, bot2, top2, ip);
}
#endif
size_t ClipperOffset::CalcSolutionCapacity()
{
size_t result = 0;
for (const Group& g : groups_)
result += (g.end_type == EndType::Joined) ? g.paths_in.size() * 2 : g.paths_in.size();
return result;
}
bool ClipperOffset::CheckReverseOrientation()
{
// nb: this assumes there's consistency in orientation between groups
bool is_reversed_orientation = false;
for (const Group& g : groups_)
if (g.end_type == EndType::Polygon)
{ {
solution.reserve(solution.size() + group.paths_in_.size()); is_reversed_orientation = g.is_reversed;
copy(group.paths_in_.begin(), group.paths_in_.end(), back_inserter(solution)); break;
}
return is_reversed_orientation;
}
void ClipperOffset::ExecuteInternal(double delta)
{
error_code_ = 0;
if (groups_.size() == 0) return;
solution->reserve(CalcSolutionCapacity());
if (std::abs(delta) < 0.5) // ie: offset is insignificant
{
Paths64::size_type sol_size = 0;
for (const Group& group : groups_) sol_size += group.paths_in.size();
solution->reserve(sol_size);
for (const Group& group : groups_)
copy(group.paths_in.begin(), group.paths_in.end(), back_inserter(*solution));
}
else
{
temp_lim_ = (miter_limit_ <= 1) ?
2.0 :
2.0 / (miter_limit_ * miter_limit_);
delta_ = delta;
std::vector<Group>::iterator git;
for (git = groups_.begin(); git != groups_.end(); ++git)
{
DoGroupOffset(*git);
if (!error_code_) continue; // all OK
solution->clear();
} }
return solution;
} }
temp_lim_ = (miter_limit_ <= 1) ? if (!solution->size()) return;
2.0 :
2.0 / (miter_limit_ * miter_limit_);
std::vector<Group>::iterator groups_iter; bool paths_reversed = CheckReverseOrientation();
for (groups_iter = groups_.begin(); //clean up self-intersections ...
groups_iter != groups_.end(); ++groups_iter) Clipper64 c;
c.PreserveCollinear(false);
//the solution should retain the orientation of the input
c.ReverseSolution(reverse_solution_ != paths_reversed);
#ifdef USINGZ
auto fp = std::bind(&ClipperOffset::ZCB, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4, std::placeholders::_5);
c.SetZCallback(fp);
#endif
c.AddSubject(*solution);
if (solution_tree)
{ {
DoGroupOffset(*groups_iter, delta); if (paths_reversed)
} c.Execute(ClipType::Union, FillRule::Negative, *solution_tree);
if (merge_groups_ && groups_.size() > 0)
{
//clean up self-intersections ...
Clipper64 c;
c.PreserveCollinear = false;
//the solution should retain the orientation of the input
c.ReverseSolution = reverse_solution_ != groups_[0].is_reversed_;
c.AddSubject(solution);
if (groups_[0].is_reversed_)
c.Execute(ClipType::Union, FillRule::Negative, solution);
else else
c.Execute(ClipType::Union, FillRule::Positive, solution); c.Execute(ClipType::Union, FillRule::Positive, *solution_tree);
} }
return solution; else
{
if (paths_reversed)
c.Execute(ClipType::Union, FillRule::Negative, *solution);
else
c.Execute(ClipType::Union, FillRule::Positive, *solution);
}
}
void ClipperOffset::Execute(double delta, Paths64& paths64)
{
paths64.clear();
solution = &paths64;
solution_tree = nullptr;
ExecuteInternal(delta);
}
void ClipperOffset::Execute(double delta, PolyTree64& polytree)
{
polytree.Clear();
solution_tree = &polytree;
solution = new Paths64();
ExecuteInternal(delta);
delete solution;
solution = nullptr;
}
void ClipperOffset::Execute(DeltaCallback64 delta_cb, Paths64& paths)
{
deltaCallback64_ = delta_cb;
Execute(1.0, paths);
} }
} // namespace } // namespace

File diff suppressed because it is too large Load Diff

View File

@@ -215,5 +215,7 @@ namespace ImGui
const wchar_t ClipboardBtnIcon = 0x0848; const wchar_t ClipboardBtnIcon = 0x0848;
const wchar_t ClipboardBtnDarkIcon = 0x0849; const wchar_t ClipboardBtnDarkIcon = 0x0849;
// void MyFunction(const char* name, const MyMatrix44& v); // void MyFunction(const char* name, const MyMatrix44& v);
const wchar_t FilamentGreen = 0x0850;
} }

View File

@@ -2134,7 +2134,7 @@ bool ImGui::QDTBeginCombo(const char *label, const char *preview_value, ImGuiCom
bool hovered, held; bool hovered, held;
bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held); bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held);
bool push_color_count = 0; unsigned int push_color_count = 0;
if (hovered || g.ActiveId == id) { if (hovered || g.ActiveId == id) {
ImGui::PushStyleColor(ImGuiCol_Border, GetColorU32(ImGuiCol_BorderActive)); ImGui::PushStyleColor(ImGuiCol_Border, GetColorU32(ImGuiCol_BorderActive));
push_color_count = 1; push_color_count = 1;
@@ -4170,7 +4170,7 @@ bool ImGui::QDTInputScalar(const char *label, ImGuiDataType data_type, void *p_d
// We are only allowed to access the state if we are already the active widget. // We are only allowed to access the state if we are already the active widget.
ImGuiInputTextState *state = GetInputTextState(id); ImGuiInputTextState *state = GetInputTextState(id);
bool push_color_count = 0; unsigned int push_color_count = 0;
if (hovered || g.ActiveId == id) { if (hovered || g.ActiveId == id) {
ImGui::PushStyleColor(ImGuiCol_Border, GetColorU32(ImGuiCol_BorderActive)); ImGui::PushStyleColor(ImGuiCol_Border, GetColorU32(ImGuiCol_BorderActive));
push_color_count = 1; push_color_count = 1;

View File

@@ -1556,7 +1556,9 @@ STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codep
return 0; // not found return 0; // not found
} }
// @TODO // @TODO
#ifndef __APPLE__
STBTT_assert(0); STBTT_assert(0);
#endif
return 0; return 0;
} }

View File

@@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 2.8.12)
project(imguizmo)
add_library(imguizmo STATIC
ImGuizmo.h
ImGuizmo.cpp
)
target_link_libraries(imguizmo PRIVATE imgui)

3148
src/imguizmo/ImGuizmo.cpp Normal file

File diff suppressed because it is too large Load Diff

295
src/imguizmo/ImGuizmo.h Normal file
View File

@@ -0,0 +1,295 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// -------------------------------------------------------------------------------------------
// History :
// 2019/11/03 View gizmo
// 2016/09/11 Behind camera culling. Scaling Delta matrix not multiplied by source matrix scales. local/world rotation and translation fixed. Display message is incorrect (X: ... Y:...) in local mode.
// 2016/09/09 Hatched negative axis. Snapping. Documentation update.
// 2016/09/04 Axis switch and translation plan autohiding. Scale transform stability improved
// 2016/09/01 Mogwai changed to Manipulate. Draw debug cube. Fixed inverted scale. Mixing scale and translation/rotation gives bad results.
// 2016/08/31 First version
//
// -------------------------------------------------------------------------------------------
// Future (no order):
//
// - Multi view
// - display rotation/translation/scale infos in local/world space and not only local
// - finish local/world matrix application
// - OPERATION as bitmask
//
// -------------------------------------------------------------------------------------------
// Example
#if 0
void EditTransform(const Camera& camera, matrix_t& matrix)
{
static ImGuizmo::OPERATION mCurrentGizmoOperation(ImGuizmo::ROTATE);
static ImGuizmo::MODE mCurrentGizmoMode(ImGuizmo::WORLD);
if (ImGui::IsKeyPressed(90))
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
if (ImGui::IsKeyPressed(69))
mCurrentGizmoOperation = ImGuizmo::ROTATE;
if (ImGui::IsKeyPressed(82)) // r Key
mCurrentGizmoOperation = ImGuizmo::SCALE;
if (ImGui::RadioButton("Translate", mCurrentGizmoOperation == ImGuizmo::TRANSLATE))
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
ImGui::SameLine();
if (ImGui::RadioButton("Rotate", mCurrentGizmoOperation == ImGuizmo::ROTATE))
mCurrentGizmoOperation = ImGuizmo::ROTATE;
ImGui::SameLine();
if (ImGui::RadioButton("Scale", mCurrentGizmoOperation == ImGuizmo::SCALE))
mCurrentGizmoOperation = ImGuizmo::SCALE;
float matrixTranslation[3], matrixRotation[3], matrixScale[3];
ImGuizmo::DecomposeMatrixToComponents(matrix.m16, matrixTranslation, matrixRotation, matrixScale);
ImGui::InputFloat3("Tr", matrixTranslation, 3);
ImGui::InputFloat3("Rt", matrixRotation, 3);
ImGui::InputFloat3("Sc", matrixScale, 3);
ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, matrix.m16);
if (mCurrentGizmoOperation != ImGuizmo::SCALE)
{
if (ImGui::RadioButton("Local", mCurrentGizmoMode == ImGuizmo::LOCAL))
mCurrentGizmoMode = ImGuizmo::LOCAL;
ImGui::SameLine();
if (ImGui::RadioButton("World", mCurrentGizmoMode == ImGuizmo::WORLD))
mCurrentGizmoMode = ImGuizmo::WORLD;
}
static bool useSnap(false);
if (ImGui::IsKeyPressed(83))
useSnap = !useSnap;
ImGui::Checkbox("", &useSnap);
ImGui::SameLine();
vec_t snap;
switch (mCurrentGizmoOperation)
{
case ImGuizmo::TRANSLATE:
snap = config.mSnapTranslation;
ImGui::InputFloat3("Snap", &snap.x);
break;
case ImGuizmo::ROTATE:
snap = config.mSnapRotation;
ImGui::InputFloat("Angle Snap", &snap.x);
break;
case ImGuizmo::SCALE:
snap = config.mSnapScale;
ImGui::InputFloat("Scale Snap", &snap.x);
break;
}
ImGuiIO& io = ImGui::GetIO();
ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
ImGuizmo::Manipulate(camera.mView.m16, camera.mProjection.m16, mCurrentGizmoOperation, mCurrentGizmoMode, matrix.m16, NULL, useSnap ? &snap.x : NULL);
}
#endif
#pragma once
#ifdef USE_IMGUI_API
#include "imconfig.h"
#endif
#ifndef IMGUI_API
#define IMGUI_API
#endif
#ifndef IMGUIZMO_NAMESPACE
#define IMGUIZMO_NAMESPACE ImGuizmo
#endif
namespace IMGUIZMO_NAMESPACE
{
// call inside your own window and before Manipulate() in order to draw gizmo to that window.
// Or pass a specific ImDrawList to draw to (e.g. ImGui::GetForegroundDrawList()).
IMGUI_API void SetDrawlist(ImDrawList* drawlist = nullptr);
// call BeginFrame right after ImGui_XXXX_NewFrame();
IMGUI_API void BeginFrame();
// this is necessary because when imguizmo is compiled into a dll, and imgui into another
// globals are not shared between them.
// More details at https://stackoverflow.com/questions/19373061/what-happens-to-global-and-static-variables-in-a-shared-library-when-it-is-dynam
// expose method to set imgui context
IMGUI_API void SetImGuiContext(ImGuiContext* ctx);
// return true if mouse cursor is over any gizmo control (axis, plan or screen component)
IMGUI_API bool IsOver();
// return true if mouse IsOver or if the gizmo is in moving state
IMGUI_API bool IsUsing();
// return true if any gizmo is in moving state
IMGUI_API bool IsUsingAny();
// enable/disable the gizmo. Stay in the state until next call to Enable.
// gizmo is rendered with gray half transparent color when disabled
IMGUI_API void Enable(bool enable);
// helper functions for manualy editing translation/rotation/scale with an input float
// translation, rotation and scale float points to 3 floats each
// Angles are in degrees (more suitable for human editing)
// example:
// float matrixTranslation[3], matrixRotation[3], matrixScale[3];
// ImGuizmo::DecomposeMatrixToComponents(gizmoMatrix.m16, matrixTranslation, matrixRotation, matrixScale);
// ImGui::InputFloat3("Tr", matrixTranslation, 3);
// ImGui::InputFloat3("Rt", matrixRotation, 3);
// ImGui::InputFloat3("Sc", matrixScale, 3);
// ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, gizmoMatrix.m16);
//
// These functions have some numerical stability issues for now. Use with caution.
IMGUI_API void DecomposeMatrixToComponents(const float* matrix, float* translation, float* rotation, float* scale);
IMGUI_API void RecomposeMatrixFromComponents(const float* translation, const float* rotation, const float* scale, float* matrix);
IMGUI_API void SetRect(float x, float y, float width, float height);
// default is false
IMGUI_API void SetOrthographic(bool isOrthographic);
// Render a cube with face color corresponding to face normal. Usefull for debug/tests
IMGUI_API void DrawCubes(const float* view, const float* projection, const float* matrices, int matrixCount);
IMGUI_API void DrawGrid(const float* view, const float* projection, const float* matrix, const float gridSize);
// call it when you want a gizmo
// Needs view and projection matrices.
// matrix parameter is the source matrix (where will be gizmo be drawn) and might be transformed by the function. Return deltaMatrix is optional
// translation is applied in world space
enum OPERATION
{
TRANSLATE_X = (1u << 0),
TRANSLATE_Y = (1u << 1),
TRANSLATE_Z = (1u << 2),
ROTATE_X = (1u << 3),
ROTATE_Y = (1u << 4),
ROTATE_Z = (1u << 5),
ROTATE_SCREEN = (1u << 6),
SCALE_X = (1u << 7),
SCALE_Y = (1u << 8),
SCALE_Z = (1u << 9),
BOUNDS = (1u << 10),
SCALE_XU = (1u << 11),
SCALE_YU = (1u << 12),
SCALE_ZU = (1u << 13),
TRANSLATE = TRANSLATE_X | TRANSLATE_Y | TRANSLATE_Z,
ROTATE = ROTATE_X | ROTATE_Y | ROTATE_Z | ROTATE_SCREEN,
SCALE = SCALE_X | SCALE_Y | SCALE_Z,
SCALEU = SCALE_XU | SCALE_YU | SCALE_ZU, // universal
UNIVERSAL = TRANSLATE | ROTATE | SCALEU
};
inline OPERATION operator|(OPERATION lhs, OPERATION rhs)
{
return static_cast<OPERATION>(static_cast<int>(lhs) | static_cast<int>(rhs));
}
enum MODE
{
LOCAL,
WORLD
};
IMGUI_API bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix = NULL, const float* snap = NULL, const float* localBounds = NULL, const float* boundsSnap = NULL);
//
// Please note that this cubeview is patented by Autodesk : https://patents.google.com/patent/US7782319B2/en
// It seems to be a defensive patent in the US. I don't think it will bring troubles using it as
// other software are using the same mechanics. But just in case, you are now warned!
//
IMGUI_API bool ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor);
// use this version if you did not call Manipulate before and you are just using ViewManipulate
IMGUI_API bool ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor);
IMGUI_API void SetID(int id);
// return true if the cursor is over the operation's gizmo
IMGUI_API bool IsOver(OPERATION op);
IMGUI_API void SetGizmoSizeClipSpace(float value);
// Allow axis to flip
// When true (default), the guizmo axis flip for better visibility
// When false, they always stay along the positive world/local axis
IMGUI_API void AllowAxisFlip(bool value);
// Configure the limit where axis are hidden
IMGUI_API void SetAxisLimit(float value);
// Configure the limit where planes are hiden
IMGUI_API void SetPlaneLimit(float value);
enum COLOR
{
DIRECTION_X, // directionColor[0]
DIRECTION_Y, // directionColor[1]
DIRECTION_Z, // directionColor[2]
PLANE_X, // planeColor[0]
PLANE_Y, // planeColor[1]
PLANE_Z, // planeColor[2]
SELECTION, // selectionColor
INACTIVE, // inactiveColor
TRANSLATION_LINE, // translationLineColor
SCALE_LINE,
ROTATION_USING_BORDER,
ROTATION_USING_FILL,
HATCHED_AXIS_LINES,
TEXT,
TEXT_SHADOW,
FACE,
COUNT
};
enum Axis
{
Axis_X,
Axis_Y,
Axis_Z,
Axis_COUNT,
};
enum FACES
{
FACE_BACK,
FACE_TOP,
FACE_RIGHT,
FACE_FRONT,
FACE_BOTTOM,
FACE_LEFT,
FACES_COUNT
};
struct Style
{
IMGUI_API Style();
float TranslationLineThickness; // Thickness of lines for translation gizmo
float TranslationLineArrowSize; // Size of arrow at the end of lines for translation gizmo
float RotationLineThickness; // Thickness of lines for rotation gizmo
float RotationOuterLineThickness; // Thickness of line surrounding the rotation gizmo
float ScaleLineThickness; // Thickness of lines for scale gizmo
float ScaleLineCircleSize; // Size of circle at the end of lines for scale gizmo
float HatchedAxisLineThickness; // Thickness of hatched axis lines
float CenterCircleSize; // Size of circle at the center of the translate/scale gizmo
ImVec4 Colors[COLOR::COUNT];
char AxisLabels[Axis::Axis_COUNT][32];
char FaceLabels[FACES::FACES_COUNT][32];
};
IMGUI_API Style& GetStyle();
}

21
src/imguizmo/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Cedric Guillemet
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

192
src/imguizmo/README.md Normal file
View File

@@ -0,0 +1,192 @@
# ImGuizmo
Latest stable tagged version is 1.83. Current master version is 1.84 WIP.
What started with the gizmo is now a collection of dear imgui widgets and more advanced controls.
## Guizmos
### ImViewGizmo
Manipulate view orientation with 1 single line of code
![Image of ImViewGizmo](http://i.imgur.com/7UVcyDd.gif)
### ImGuizmo
ImGizmo is a small (.h and .cpp) library built ontop of Dear ImGui that allow you to manipulate(Rotate & translate at the moment) 4x4 float matrices. No other dependancies. Coded with Immediate Mode (IM) philosophy in mind.
Built against DearImgui 1.53WIP
![Image of Rotation](http://i.imgur.com/y4mcVoT.gif)
![Image of Translation](http://i.imgur.com/o8q8iHq.gif)
![Image of Bounds](http://i.imgur.com/3Ez5LBr.gif)
There is now a sample for Win32/OpenGL ! With a binary in bin directory.
![Image of Sample](https://i.imgur.com/nXlzyqD.png)
### ImSequencer
A WIP little sequencer used to edit frame start/end for different events in a timeline.
![Image of Rotation](http://i.imgur.com/BeyNwCn.png)
Check the sample for the documentation. More to come...
### Graph Editor
Nodes + connections. Custom draw inside nodes is possible with the delegate system in place.
![Image of GraphEditor](Images/nodeeditor.jpg)
### API doc
Call BeginFrame right after ImGui_XXXX_NewFrame();
```C++
void BeginFrame();
```
return true if mouse cursor is over any gizmo control (axis, plan or screen component)
```C++
bool IsOver();**
```
return true if mouse IsOver or if the gizmo is in moving state
```C++
bool IsUsing();**
```
enable/disable the gizmo. Stay in the state until next call to Enable. gizmo is rendered with gray half transparent color when disabled
```C++
void Enable(bool enable);**
```
helper functions for manualy editing translation/rotation/scale with an input float
translation, rotation and scale float points to 3 floats each
Angles are in degrees (more suitable for human editing)
example:
```C++
float matrixTranslation[3], matrixRotation[3], matrixScale[3];
ImGuizmo::DecomposeMatrixToComponents(gizmoMatrix.m16, matrixTranslation, matrixRotation, matrixScale);
ImGui::InputFloat3("Tr", matrixTranslation, 3);
ImGui::InputFloat3("Rt", matrixRotation, 3);
ImGui::InputFloat3("Sc", matrixScale, 3);
ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, gizmoMatrix.m16);
```
These functions have some numerical stability issues for now. Use with caution.
```C++
void DecomposeMatrixToComponents(const float *matrix, float *translation, float *rotation, float *scale);
void RecomposeMatrixFromComponents(const float *translation, const float *rotation, const float *scale, float *matrix);**
```
Render a cube with face color corresponding to face normal. Usefull for debug/test
```C++
void DrawCube(const float *view, const float *projection, float *matrix);**
```
Call it when you want a gizmo
Needs view and projection matrices.
Matrix parameter is the source matrix (where will be gizmo be drawn) and might be transformed by the function. Return deltaMatrix is optional. snap points to a float[3] for translation and to a single float for scale or rotation. Snap angle is in Euler Degrees.
```C++
enum OPERATION
{
TRANSLATE,
ROTATE,
SCALE
};
enum MODE
{
LOCAL,
WORLD
};
void Manipulate(const float *view, const float *projection, OPERATION operation, MODE mode, float *matrix, float *deltaMatrix = 0, float *snap = 0);**
```
### ImGui Example
Code for :
![Image of dialog](http://i.imgur.com/GL5flN1.png)
```C++
void EditTransform(const Camera& camera, matrix_t& matrix)
{
static ImGuizmo::OPERATION mCurrentGizmoOperation(ImGuizmo::ROTATE);
static ImGuizmo::MODE mCurrentGizmoMode(ImGuizmo::WORLD);
if (ImGui::IsKeyPressed(90))
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
if (ImGui::IsKeyPressed(69))
mCurrentGizmoOperation = ImGuizmo::ROTATE;
if (ImGui::IsKeyPressed(82)) // r Key
mCurrentGizmoOperation = ImGuizmo::SCALE;
if (ImGui::RadioButton("Translate", mCurrentGizmoOperation == ImGuizmo::TRANSLATE))
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
ImGui::SameLine();
if (ImGui::RadioButton("Rotate", mCurrentGizmoOperation == ImGuizmo::ROTATE))
mCurrentGizmoOperation = ImGuizmo::ROTATE;
ImGui::SameLine();
if (ImGui::RadioButton("Scale", mCurrentGizmoOperation == ImGuizmo::SCALE))
mCurrentGizmoOperation = ImGuizmo::SCALE;
float matrixTranslation[3], matrixRotation[3], matrixScale[3];
ImGuizmo::DecomposeMatrixToComponents(matrix.m16, matrixTranslation, matrixRotation, matrixScale);
ImGui::InputFloat3("Tr", matrixTranslation, 3);
ImGui::InputFloat3("Rt", matrixRotation, 3);
ImGui::InputFloat3("Sc", matrixScale, 3);
ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, matrix.m16);
if (mCurrentGizmoOperation != ImGuizmo::SCALE)
{
if (ImGui::RadioButton("Local", mCurrentGizmoMode == ImGuizmo::LOCAL))
mCurrentGizmoMode = ImGuizmo::LOCAL;
ImGui::SameLine();
if (ImGui::RadioButton("World", mCurrentGizmoMode == ImGuizmo::WORLD))
mCurrentGizmoMode = ImGuizmo::WORLD;
}
static bool useSnap(false);
if (ImGui::IsKeyPressed(83))
useSnap = !useSnap;
ImGui::Checkbox("", &useSnap);
ImGui::SameLine();
vec_t snap;
switch (mCurrentGizmoOperation)
{
case ImGuizmo::TRANSLATE:
snap = config.mSnapTranslation;
ImGui::InputFloat3("Snap", &snap.x);
break;
case ImGuizmo::ROTATE:
snap = config.mSnapRotation;
ImGui::InputFloat("Angle Snap", &snap.x);
break;
case ImGuizmo::SCALE:
snap = config.mSnapScale;
ImGui::InputFloat("Scale Snap", &snap.x);
break;
}
ImGuiIO& io = ImGui::GetIO();
ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
ImGuizmo::Manipulate(camera.mView.m16, camera.mProjection.m16, mCurrentGizmoOperation, mCurrentGizmoMode, matrix.m16, NULL, useSnap ? &snap.x : NULL);
}
```
## Install
ImGuizmo can be installed via [vcpkg](https://github.com/microsoft/vcpkg) and used cmake
```bash
vcpkg install imguizmo
```
See the [vcpkg example](/vcpkg-example) for more details
## License
ImGuizmo is licensed under the MIT License, see [LICENSE](/LICENSE) for more information.

View File

@@ -656,7 +656,7 @@ inline std::string toString(const S& /*sh*/)
} }
template<Formats, class S> template<Formats, class S>
inline std::string serialize(const S& /*sh*/, double /*scale*/=1, std::string fill = "none", std::string stroke = "black", float stroke_width = 1) inline std::string serialize(const S & /*sh*/, const std::string &name = "", double /*scale*/ = 1, std::string fill = "none", std::string stroke = "black", float stroke_width = 1)
{ {
static_assert(always_false<S>::value, static_assert(always_false<S>::value,
"shapelike::serialize() unimplemented!"); "shapelike::serialize() unimplemented!");

View File

@@ -10,6 +10,7 @@
#include <libnest2d/geometry_traits.hpp> #include <libnest2d/geometry_traits.hpp>
#define LARGE_COST_TO_REJECT 1e7 #define LARGE_COST_TO_REJECT 1e7
#define COST_OF_NEW_PLATE 0.1
namespace libnest2d { namespace libnest2d {
@@ -75,8 +76,8 @@ class _Item {
public: public:
int itemid_{ 0 }; int itemid_{ 0 };
std::vector<int> extrude_ids; std::map<int, std::string> extrude_id_filament_types; // extrude id to filament type
int filament_temp_type = -1; // -1 means unset. otherwise should be {0,1,2} int filament_temp_type = -1; // -1 means unset. otherwise should be one of FilamentTempType ie {0,1,2}
double height{ 0 }; double height{ 0 };
double print_temp{ 0 }; double print_temp{ 0 };
double bed_temp{ 0 }; double bed_temp{ 0 };
@@ -85,7 +86,9 @@ public:
//QDS: virtual object to mark unprintable region on heatbed //QDS: virtual object to mark unprintable region on heatbed
bool is_virt_object{ false }; bool is_virt_object{ false };
bool is_wipe_tower{ false }; bool is_wipe_tower{ false };
bool has_tried_with_excluded{ false }; bool is_extrusion_cali_object{ false };
bool has_tried_without_extrusion_cali_obj{ false };
std::vector<double> allowed_rotations{0.};
/// The type of the shape which was handed over as the template argument. /// The type of the shape which was handed over as the template argument.
using ShapeType = RawShape; using ShapeType = RawShape;

View File

@@ -18,8 +18,8 @@
#include "placer_boilerplate.hpp" #include "placer_boilerplate.hpp"
// temporary // temporary
//#include "../tools/svgtools.hpp" #include "../tools/svgtools.hpp"
//#include <iomanip> // setprecision #include <iomanip> // setprecision
#include <libnest2d/parallel.hpp> #include <libnest2d/parallel.hpp>
@@ -130,6 +130,9 @@ struct NfpPConfig {
//QDS: sort function for selector //QDS: sort function for selector
std::function<bool(_Item<RawShape>& i1, _Item<RawShape>& i2)> sortfunc; std::function<bool(_Item<RawShape>& i1, _Item<RawShape>& i2)> sortfunc;
std::function<void(const std::string &)> progressFunc = {};
//QDS: excluded region for V4 bed //QDS: excluded region for V4 bed
std::vector<_Item<RawShape> > m_excluded_regions; std::vector<_Item<RawShape> > m_excluded_regions;
_ItemGroup<RawShape> m_excluded_items; _ItemGroup<RawShape> m_excluded_items;
@@ -557,10 +560,12 @@ public:
template<class Range = ConstItemRange<typename Base::DefaultIter>> template<class Range = ConstItemRange<typename Base::DefaultIter>>
PackResult trypack(Item& item, PackResult trypack(Item& item,
const Range& remaining = Range()) { const Range& remaining = Range()) {
auto result = _trypack(item, remaining); if (item.is_wipe_tower) {
PackResult result1 = _trypack_with_original_pos(item);
if (result1.score() >= 0 && result1.score() < LARGE_COST_TO_REJECT) return result1;
}
// Experimental auto result = _trypack(item, remaining);
// if(!result) repack(item, result);
return result; return result;
} }
@@ -685,11 +690,20 @@ private:
}; };
using Edges = EdgeCache<RawShape>; using Edges = EdgeCache<RawShape>;
// item won't overlap with virtual objects if it's inside or touches NFP
// @return 1 if current item overlaps with virtual objects, 0 otherwise
bool overlapWithVirtObject(const Item& item, const Box& binbb){
if (items_.empty()) return 0;
Shapes nfps = calcnfp(item, binbb, Lvl<MaxNfpLevel::value>());
auto v = item.referenceVertex();
for (const RawShape &nfp : nfps) {
if (sl::isInside(v, nfp) || sl::touches(v, nfp)) { return false; }
}
return true;
};
template<class Range = ConstItemRange<typename Base::DefaultIter>> template<class Range = ConstItemRange<typename Base::DefaultIter>>
PackResult _trypack( PackResult _trypack(Item& item, const Range& remaining = Range()) {
Item& item,
const Range& remaining = Range()) {
PackResult ret; PackResult ret;
@@ -755,41 +769,41 @@ private:
}; };
} }
bool first_object = std::all_of(items_.begin(), items_.end(), [&](const Item &rawShape) { return rawShape.is_virt_object && !rawShape.is_wipe_tower; }); for (auto &it : items_) {
config_.progressFunc((boost::format("_trypack: plate: %4%, existing object: %1%, pos: (%2%, %3%)") % it.get().name % unscale_(it.get().translation()[0]) %
unscale_(it.get().translation()[1]) % plateID())
.str());
}
// item won't overlap with virtual objects if it's inside or touches NFP bool first_object = std::all_of(items_.begin(), items_.end(), [&](const Item &rawShape) { return rawShape.is_virt_object && !rawShape.is_wipe_tower; });
// @return 1 if current item overlaps with virtual objects, 0 otherwise
auto overlapWithVirtObject = [&]() -> double {
if (items_.empty()) return 0;
nfps = calcnfp(item, binbb, Lvl<MaxNfpLevel::value>());
auto v = item.referenceVertex();
for (const RawShape &nfp : nfps) {
if (sl::isInside(v, nfp) || sl::touches(v, nfp)) { return 0; }
}
return 1;
};
if (first_object) { if (first_object) {
setInitialPosition(item); setInitialPosition(item);
auto best_tr = item.translation(); auto best_tr = item.translation();
auto best_rot = item.rotation(); auto best_rot = item.rotation();
best_overfit = overfit(item.transformedShape(), bin_) + overlapWithVirtObject(); best_overfit = std::numeric_limits<double>::max();
// try normal inflation first, then 0 inflation if not fit. See STUDIO-5566. // try normal inflation first, then 0 inflation if not fit. See STUDIO-5566.
// Note for by-object printing, bed is expanded by -config_.bed_shrink.x(). // Note for by-object printing, bed is expanded by -config_.bed_shrink.x().
Coord inflation_back = item.inflation(); Coord inflation_back = item.inflation();
Coord inflations[2]={inflation_back, std::abs(config_.bed_shrink.x())}; Coord inflations[2]={inflation_back, 0};
for (size_t i = 0; i < 2; i++) { for (size_t i = 0; i < 2; i++) {
item.inflation(inflations[i]); item.inflation(inflations[i]);
for (auto rot : config_.rotations) { for (auto rot : item.allowed_rotations) {
item.translation(initial_tr); item.translation(initial_tr);
item.rotation(initial_rot + rot); item.rotation(initial_rot + rot);
setInitialPosition(item); setInitialPosition(item);
double of = overfit(item.transformedShape(), bin_); if (!overlapWithVirtObject(item, binbb)) {
if (of + overlapWithVirtObject() < best_overfit) { double of = overfit(item.transformedShape(), bin_);
best_overfit = of; if (of < best_overfit) {
best_tr = item.translation(); best_overfit = of;
best_rot = item.rotation(); best_tr = item.translation();
best_rot = item.rotation();
if (best_overfit <= 0) {
config_.progressFunc("First object " + item.name + " can fit with rot=" + std::to_string(rot));
break;
}
}
} }
} }
can_pack = best_overfit <= 0; can_pack = best_overfit <= 0;
@@ -806,7 +820,7 @@ private:
Pile merged_pile = merged_pile_; Pile merged_pile = merged_pile_;
for(auto rot : config_.rotations) { for(auto rot : item.allowed_rotations) {
item.translation(initial_tr); item.translation(initial_tr);
item.rotation(initial_rot + rot); item.rotation(initial_rot + rot);
@@ -991,6 +1005,7 @@ private:
final_rot = initial_rot + rot; final_rot = initial_rot + rot;
can_pack = true; can_pack = true;
global_score = best_score; global_score = best_score;
break;
} }
} }
@@ -998,41 +1013,7 @@ private:
item.rotation(final_rot); item.rotation(final_rot);
} }
#ifdef SVGTOOLS_HPP if (config_.save_svg) saveSVG(binbb, nfps, item, global_score, can_pack);
if (config_.save_svg) {
svg::SVGWriter<RawShape> svgwriter;
Box binbb2(binbb.width() * 2, binbb.height() * 2, binbb.center()); // expand bbox to allow object be drawed outside
svgwriter.setSize(binbb2);
svgwriter.conf_.x0 = binbb.width();
svgwriter.conf_.y0 = -binbb.height() / 2; // origin is top left corner
svgwriter.add_comment("bed");
svgwriter.writeShape(box2RawShape(binbb), "none", "black");
svgwriter.add_comment("nfps");
for (int i = 0; i < nfps.size(); i++) svgwriter.writeShape(nfps[i], "none", "blue");
for (int i = 0; i < items_.size(); i++) {
svgwriter.add_comment(items_[i].get().name);
svgwriter.writeItem(items_[i], "none", "black");
}
svgwriter.add_comment("merged_pile_");
for (int i = 0; i < merged_pile_.size(); i++) svgwriter.writeShape(merged_pile_[i], "none", "yellow");
svgwriter.add_comment("current item");
svgwriter.writeItem(item, "red", "red", 2);
std::stringstream ss;
ss.setf(std::ios::fixed | std::ios::showpoint);
ss.precision(1);
ss << "t=" << round(item.translation().x() / 1e6) << ","
<< round(item.translation().y() / 1e6)
//<< "-rot=" << round(item.rotation().toDegrees())
<< "-sco=" << round(global_score);
svgwriter.draw_text(20, 20, ss.str(), "blue", 20);
ss.str("");
ss << "items.size=" << items_.size() << "-merged_pile.size=" << merged_pile_.size();
svgwriter.draw_text(20, 40, ss.str(), "blue", 20);
svgwriter.save(boost::filesystem::path("C:/Users/arthur.tang/AppData/Roaming/QIDIStudioInternal/SVG") /
("nfpplacer_" + std::to_string(plate_id) + "_" + ss.str() + "_" + item.name + ".svg"));
}
#endif
if(can_pack) { if(can_pack) {
ret = PackResult(item); ret = PackResult(item);
@@ -1045,6 +1026,87 @@ private:
return ret; return ret;
} }
PackResult _trypack_with_original_pos(Item &item)
{
PackResult ret;
bool can_pack = false;
double best_overfit = std::numeric_limits<double>::max();
double global_score = std::numeric_limits<double>::max();
auto initial_tr = item.translation();
auto initial_rot = item.rotation();
Vertex final_tr = initial_tr;
Radians final_rot = initial_rot;
Shapes nfps;
auto binbb = sl::boundingBox(bin_);
for (auto &it : items_) {
config_.progressFunc((boost::format("_trypack_with_original_pos: plate: %4%, existing object: %1%, pos: (%2%, %3%)") % it.get().name % unscale_(it.get().translation()[0]) %
unscale_(it.get().translation()[1]) % plateID())
.str());
}
{
for (auto rot : item.allowed_rotations) {
item.translation(initial_tr);
item.rotation(initial_rot + rot);
if (!overlapWithVirtObject(item, binbb)) {
can_pack = true;
final_tr = initial_tr;
final_rot = initial_rot + rot;
global_score = 0.3;
break;
}
}
item.translation(final_tr);
item.rotation(final_rot);
}
if (config_.save_svg) saveSVG(binbb, nfps, item, global_score, can_pack);
if (can_pack) {
ret = PackResult(item);
ret.score_ = global_score;
// merged_pile_ = nfp::merge(merged_pile_, item.transformedShape());
config_.progressFunc((boost::format("_trypack_with_original_pos: item %1% can pack") % item.name).str());
} else {
ret = PackResult(best_overfit);
}
return ret;
}
void saveSVG(Box &binbb, Shapes &nfps, Item &item, double global_score, bool can_pack)
{
svg::SVGWriter<RawShape> svgwriter;
Box binbb2(binbb.width() * 2, binbb.height() * 2, binbb.center()); // expand bbox to allow object be drawed outside
svgwriter.setSize(binbb2);
svgwriter.conf_.x0 = binbb.width();
svgwriter.conf_.y0 = -binbb.height() / 2; // origin is top left corner
svgwriter.writeShape(box2RawShape(binbb), "bed", "none", "black");
for (int i = 0; i < nfps.size(); i++) svgwriter.writeShape(nfps[i], "nfp_" + std::to_string(i), "none", "blue", 0.2);
for (int i = 0; i < items_.size(); i++) { svgwriter.writeItem(items_[i], items_[i].get().name, "none", "black", 0.2); }
for (int i = 0; i < merged_pile_.size(); i++) svgwriter.writeShape(merged_pile_[i], "merged_pile_" + std::to_string(i), "none", "yellow", 0.2);
svgwriter.writeItem(item, item.name, "red", "red", 0.3);
std::stringstream ss;
ss.setf(std::ios::fixed | std::ios::showpoint);
ss.precision(1);
ss << "t=" << round(item.translation().x() / 1e6) << ","
<< round(item.translation().y() / 1e6)
//<< "-rot=" << round(item.rotation().toDegrees())
<< "-sco=" << round(global_score);
svgwriter.draw_text(20, 20, ss.str(), "blue", 10);
ss.str("");
ss << "items.size=" << items_.size() << "-merged_pile.size=" << merged_pile_.size();
svgwriter.draw_text(20, 40, ss.str(), "blue", 10);
svgwriter.save(boost::filesystem::path("SVG") / ("plate_" + std::to_string(plate_id) + "_" + ss.str() + "_" + item.name + "_canPack=" + std::to_string(can_pack) + ".svg"));
}
RawShape box2RawShape(Box& bbin) RawShape box2RawShape(Box& bbin)
{ {
RawShape binrsh; RawShape binrsh;
@@ -1158,16 +1220,7 @@ private:
auto d = cb - ci; auto d = cb - ci;
// QDS make sure the item won't clash with excluded regions // QDS make sure the item won't clash with excluded regions
// do we have wipe tower after arranging?
size_t n_objs = 0; size_t n_objs = 0;
std::set<int> extruders;
for (const Item& item : items_) {
if (!item.is_virt_object) {
extruders.insert(item.extrude_ids.begin(), item.extrude_ids.end());
n_objs++;
}
}
bool need_wipe_tower = extruders.size() > 1;
std::vector<RawShape> objs,excludes; std::vector<RawShape> objs,excludes;
for (Item &item : items_) { for (Item &item : items_) {

View File

@@ -41,7 +41,7 @@ public:
std::vector<Placer> placers; std::vector<Placer> placers;
placers.reserve(last-first); placers.reserve(last-first);
typename Base::PackGroup fixed_bins; typename Base::PackGroup fixed_bins;
std::for_each(first, last, [this, &fixed_bins](Item& itm) { std::for_each(first, last, [this, &fixed_bins](Item& itm) {
if (itm.isFixed()) { if (itm.isFixed()) {
@@ -67,7 +67,7 @@ public:
std::for_each(first, last, [this,&svgwriter](Item &itm) { svgwriter.writeShape(itm, "none", "blue"); }); std::for_each(first, last, [this,&svgwriter](Item &itm) { svgwriter.writeShape(itm, "none", "blue"); });
svgwriter.save(boost::filesystem::path("SVG") / "all_items.svg"); svgwriter.save(boost::filesystem::path("SVG") / "all_items.svg");
#endif #endif
std::function<bool(Item& i1, Item& i2)> sortfunc; std::function<bool(Item& i1, Item& i2)> sortfunc;
if (pconfig.sortfunc) if (pconfig.sortfunc)
sortfunc = pconfig.sortfunc; sortfunc = pconfig.sortfunc;
@@ -88,7 +88,23 @@ public:
for (auto it = store_.begin(); it != store_.end(); ++it) { for (auto it = store_.begin(); it != store_.end(); ++it) {
std::stringstream ss; std::stringstream ss;
ss << "initial order: " << it->get().name << ", p=" << it->get().priority() << ", bed_temp=" << it->get().bed_temp << ", height=" << it->get().height ss << "initial order: " << it->get().name << ", p=" << it->get().priority() << ", bed_temp=" << it->get().bed_temp << ", height=" << it->get().height
<< ", area=" << it->get().area(); << ", area=" << it->get().area() << ", allowed_rotations=";
for(auto r: it->get().allowed_rotations) ss << r << ", ";
if (this->unfitindicator_)
this->unfitindicator_(ss.str());
}
// debug: write down fixed items
for (size_t i = 0; i < fixed_bins.size(); i++) {
if (fixed_bins[i].empty())
continue;
std::stringstream ss;
ss << "fixed bin " << i << ": ";
for (auto it = fixed_bins[i].begin(); it != fixed_bins[i].end(); ++it) {
ss << it->get().name << ", p=" << it->get().priority() << ", bed_temp=" << it->get().bed_temp << ", height=" << it->get().height
<< ", area=" << it->get().area() << ", allowed_rotations=";
for(auto r: it->get().allowed_rotations) ss << r << ", ";
ss << "; ";
}
if (this->unfitindicator_) if (this->unfitindicator_)
this->unfitindicator_(ss.str()); this->unfitindicator_(ss.str());
} }
@@ -101,8 +117,8 @@ public:
}; };
auto& cancelled = this->stopcond_; auto& cancelled = this->stopcond_;
this->template remove_unpackable_items<Placer>(store_, bin, pconfig); //this->template remove_unpackable_items<Placer>(store_, bin, pconfig);
for (auto it = store_.begin(); it != store_.end() && !cancelled(); ++it) { for (auto it = store_.begin(); it != store_.end() && !cancelled(); ++it) {
// skip unpackable item // skip unpackable item
@@ -115,13 +131,21 @@ public:
double score_all_plates = 0, score_all_plates_best = std::numeric_limits<double>::max(); double score_all_plates = 0, score_all_plates_best = std::numeric_limits<double>::max();
typename Placer::PackResult result, result_best, result_firstfit; typename Placer::PackResult result, result_best, result_firstfit;
int j = 0; int j = 0;
while(!was_packed && !cancelled()) { while (!was_packed && !cancelled() && placers.size() <= MAX_NUM_PLATES) {
for(; j < placers.size() && !was_packed && !cancelled(); j++) { for(; j < placers.size() && !was_packed && !cancelled() && j<MAX_NUM_PLATES; j++) {
if (it->get().is_wipe_tower && it->get().binId() != placers[j].plateID()) {
if (this->unfitindicator_)
this->unfitindicator_(it->get().name + " cant be placed in plate_id=" + std::to_string(j) + "/" + std::to_string(placers.size()) + ", continue to next plate");
continue;
}
result = placers[j].pack(*it, rem(it, store_)); result = placers[j].pack(*it, rem(it, store_));
score = result.score(); score = result.score();
score_all_plates = score; score_all_plates = score + COST_OF_NEW_PLATE * j; // add a larger cost to larger plate id to encourace to use less plates
for (int i = 0; i < placers.size(); i++) { score_all_plates += placers[i].score();} for (int i = 0; i < placers.size(); i++) { score_all_plates += placers[i].score();}
if (this->unfitindicator_) this->unfitindicator_(it->get().name + " bed_id="+std::to_string(j) + ",score=" + std::to_string(score)+", score_all_plates="+std::to_string(score_all_plates)); if (this->unfitindicator_)
this->unfitindicator_((boost::format("item %1% bed_id=%2%, score=%3%, score_all_plates=%4%, pos=(%5%, %6%)") % it->get().name % j % score %
score_all_plates % unscale_(it->get().translation()[0]) % unscale_(it->get().translation()[1]))
.str());
if(score >= 0 && score < LARGE_COST_TO_REJECT) { if(score >= 0 && score < LARGE_COST_TO_REJECT) {
if (bed_id_firstfit == -1) { if (bed_id_firstfit == -1) {
@@ -161,42 +185,42 @@ public:
makeProgress(placers[j], j); makeProgress(placers[j], j);
} }
if (was_packed && it->get().has_tried_with_excluded) { // if the object can't be packed, try to pack it without extrusion calibration object
placers[j].clearItems([](const Item &itm) { return itm.isFixed() && !itm.is_wipe_tower; }); bool placer_not_packed = !was_packed && j > 0 && j == placers.size() && placers[j - 1].getPackedSize() == 0; // large item is not placed into the bin
if (fixed_bins.size() >= placers.size())
placers[j].preload(fixed_bins[placers.size() - 1]);
}
bool placer_not_packed = !was_packed && !placers.empty() && j == placers.size() && placers[j - 1].getPackedSize() == 0; // large item is not placed into the bin
if (placer_not_packed) { if (placer_not_packed) {
if (it->get().has_tried_with_excluded == false) { if (it->get().has_tried_without_extrusion_cali_obj == false) {
it->get().has_tried_with_excluded = true; it->get().has_tried_without_extrusion_cali_obj = true;
placers[j - 1].clearItems([](const Item &itm) { return itm.isFixed()&&!itm.is_wipe_tower; }); placers[j - 1].clearItems([](const Item &itm) { return itm.is_extrusion_cali_object; });
placers[j - 1].preload(pconfig.m_excluded_items);
j = j - 1; j = j - 1;
continue; continue;
} else {
placers[j - 1].clearItems([](const Item &itm) { return itm.isFixed() && !itm.is_wipe_tower; });
placers[j - 1].preload(fixed_bins[placers.size() - 1]);
} }
} }
if(!was_packed){ if(!was_packed){
if (this->unfitindicator_ && !placers.empty()) if (this->unfitindicator_ && !placers.empty())
this->unfitindicator_(it->get().name + " not fit! height=" +std::to_string(it->get().height) this->unfitindicator_(it->get().name + " not fit! plate_id=" + std::to_string(placers.back().plateID()) + ", score=" + std::to_string(score) +
+ " ,plate_id=" + std::to_string(j-1) ", best_bed_id=" + std::to_string(best_bed_id) + ", score_all_plates=" + std::to_string(score_all_plates) +
+ ", score=" + std::to_string(score) ", item.bed_id=" + std::to_string(it->get().binId()));
+ ", best_bed_id=" + std::to_string(best_bed_id) if (!placers.empty() && placers.back().getItems().empty()) {
+ ", score_all_plates=" + std::to_string(score_all_plates) it->get().binId(BIN_ID_UNFIT);
+", overfit=" + std::to_string(result.overfit())); if (this->unfitindicator_) this->unfitindicator_(it->get().name + " can't fit into a new bin. Can't fit!");
// remove the last empty placer to force next item to be fit in existing plates first
placers.emplace_back(bin); if (placers.size() > 1) placers.pop_back();
placers.back().plateID(placers.size() - 1); break;
placers.back().configure(pconfig); }
if (fixed_bins.size() >= placers.size()) if (placers.size() < MAX_NUM_PLATES) {
placers.back().preload(fixed_bins[placers.size() - 1]); placers.emplace_back(bin);
//placers.back().preload(pconfig.m_excluded_items); placers.back().plateID(placers.size() - 1);
packed_bins_.emplace_back(); placers.back().configure(pconfig);
j = placers.size() - 1; if (fixed_bins.size() >= placers.size()) placers.back().preload(fixed_bins[placers.size() - 1]);
// placers.back().preload(pconfig.m_excluded_items);
packed_bins_.emplace_back();
j = placers.size() - 1;
} else {
it->get().binId(BIN_ID_UNFIT);
if (this->unfitindicator_) this->unfitindicator_(it->get().name + " can't fit into any bin. Can't fit!");
break;
}
} }
} }
} }

View File

@@ -443,7 +443,7 @@ inline void offset(PolygonImpl& sh, bp2d::Coord distance)
#ifndef DISABLE_BOOST_SERIALIZE #ifndef DISABLE_BOOST_SERIALIZE
template<> inline std::string serialize<libnest2d::Formats::SVG>( template<> inline std::string serialize<libnest2d::Formats::SVG>(
const PolygonImpl& sh, double scale, std::string fill, std::string stroke, float stroke_width) const PolygonImpl& sh, const std::string& name, double scale, std::string fill, std::string stroke, float stroke_width)
{ {
std::stringstream ss; std::stringstream ss;
std::string style = "fill: "+fill+"; stroke: "+stroke+"; stroke-width: "+std::to_string(stroke_width)+"px; "; std::string style = "fill: "+fill+"; stroke: "+stroke+"; stroke-width: "+std::to_string(stroke_width)+"px; ";
@@ -478,7 +478,14 @@ template<> inline std::string serialize<libnest2d::Formats::SVG>(
ss << svg_data << std::endl; ss << svg_data << std::endl;
return ss.str(); std::string svg_content = ss.str();
if (!name.empty()) {
size_t pos = svg_content.find_first_of("<g ");
if (pos != std::string::npos) { svg_content.insert(pos + 3, "id=\"" + name + "\" ");
}
}
return svg_content;
} }
#endif #endif

View File

@@ -38,6 +38,8 @@ public:
inline Radians angleToX() const { inline Radians angleToX() const {
double ret = std::atan2(getY(axis_), getX(axis_)); double ret = std::atan2(getY(axis_), getX(axis_));
// horizontal or vertical up-right rectangles are not distinguished, but we need to get the horizontal one
if (abs(ret) < EPSILON && abs(bottom_) < abs(right_)) ret += Pi / 2;
auto s = std::signbit(ret); auto s = std::signbit(ret);
if(s) ret += Pi_2; if(s) ret += Pi_2;
return -ret; return -ret;

View File

@@ -31,7 +31,7 @@ public:
double x0, y0; double x0, y0;
Config(): Config():
origo_location(BOTTOMLEFT), mm_in_coord_units(1000000), origo_location(BOTTOMLEFT), mm_in_coord_units(1000000),
width(500), height(500),x0(100) {} width(500), height(500),x0(100),y0(0) {}
}; };
@@ -47,14 +47,15 @@ public:
void setSize(const Box &box) { void setSize(const Box &box) {
conf_.x0 = box.width() / 5; conf_.x0 = box.width() / 5;
conf_.x0 = box.height() / 5; conf_.y0 = box.height() / 5;
conf_.height = static_cast<double>(box.height() + conf_.y0*2) / conf_.height = static_cast<double>(box.height() + conf_.y0*2) /
conf_.mm_in_coord_units; conf_.mm_in_coord_units;
conf_.width = static_cast<double>(box.width() + conf_.x0*2) / conf_.width = static_cast<double>(box.width() + conf_.x0*2) /
conf_.mm_in_coord_units; conf_.mm_in_coord_units;
} }
void writeShape(RawShape tsh, std::string fill = "none", std::string stroke = "black", float stroke_width = 1) { void writeShape(RawShape tsh, const std::string &name = "", std::string fill = "none", std::string stroke = "black", float stroke_width = 1)
{
if(svg_layers_.empty()) addLayer(); if(svg_layers_.empty()) addLayer();
if(conf_.origo_location == BOTTOMLEFT) { if(conf_.origo_location == BOTTOMLEFT) {
auto d = static_cast<Coord>( auto d = static_cast<Coord>(
@@ -74,13 +75,14 @@ public:
} }
} }
currentLayer() += currentLayer() +=
shapelike::serialize<Formats::SVG>(tsh, shapelike::serialize<Formats::SVG>(tsh, name,
1.0 / conf_.mm_in_coord_units, fill, stroke, stroke_width) + 1.0 / conf_.mm_in_coord_units, fill, stroke, stroke_width) +
"\n"; "\n";
} }
void writeItem(const Item& item, std::string fill = "none", std::string stroke = "black", float stroke_width = 1) { void writeItem(const Item &item, const std::string &name = "", std::string fill = "none", std::string stroke = "black", float stroke_width = 1)
writeShape(item.transformedShape(), fill, stroke, stroke_width); {
writeShape(item.transformedShape(), name, fill, stroke, stroke_width);
} }
void writePackGroup(const PackGroup& result) { void writePackGroup(const PackGroup& result) {
@@ -97,7 +99,7 @@ public:
auto it = from; auto it = from;
PackGroup pg; PackGroup pg;
while(it != to) { while(it != to) {
if(it->binId() == BIN_ID_UNSET) continue; if (it->binId() == BIN_ID_UNFIT) continue;
while(pg.size() <= size_t(it->binId())) pg.emplace_back(); while(pg.size() <= size_t(it->binId())) pg.emplace_back();
pg[it->binId()].emplace_back(*it); pg[it->binId()].emplace_back(*it);
++it; ++it;
@@ -159,6 +161,8 @@ public:
currentLayer() += "<!-- " + comment + " -->\n"; currentLayer() += "<!-- " + comment + " -->\n";
} }
void clear() { svg_layers_.clear(); }
private: private:
std::string& currentLayer() { return svg_layers_.back(); } std::string& currentLayer() { return svg_layers_.back(); }

View File

@@ -38,6 +38,7 @@
#include "mcut/mcut.h" #include "mcut/mcut.h"
#include <chrono>
#include <future> #include <future>
#include <map> #include <map>
#include <memory> #include <memory>

View File

@@ -22,6 +22,11 @@
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>@SLIC3R_BUILD_ID@</string> <string>@SLIC3R_BUILD_ID@</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>CFBundleURLTypes</key> <key>CFBundleURLTypes</key>
<array> <array>
<dict> <dict>
@@ -132,10 +137,5 @@
<key>ASAN_OPTIONS</key> <key>ASAN_OPTIONS</key>
<string>detect_container_overflow=0</string> <string>detect_container_overflow=0</string>
</dict> </dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</dict> </dict>
</plist> </plist>