From f958cfd2ff88ab788f458b6149aa30fb982d7a4a Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 27 Sep 2019 14:52:19 +0200 Subject: [PATCH 01/73] ENABLE_3DCONNEXION_DEVICES - 1st installment of support for 3Dconnexion devices Implemented using hidapi library (https://github.com/libusb/hidapi) and https://github.com/koenieee/CrossplatformSpacemouseDriver/tree/master/SpaceMouseDriver as reference Unsolved issues: - When manipulating the SpaceNavigator wxWidgets generates a mouse wheel event that needs to be filtered out - wxWidgets does not detect devices being connected/disconnected to the pc - Current state forces a continuous rendering - Current state misses dependence on camera zoom - Non intuitive movement limits - Translation and rotation speed factors are hardcoded - Number of device buttons hardcoded --- CMakeLists.txt | 19 + src/hidapi/hidapi.h | 395 +++++++++ src/hidapi/linux/hid.c | 797 ++++++++++++++++++ src/hidapi/mac/hid.c | 1121 ++++++++++++++++++++++++++ src/hidapi/win/hid.c | 956 ++++++++++++++++++++++ src/libslic3r/Technologies.hpp | 9 + src/slic3r/CMakeLists.txt | 4 +- src/slic3r/GUI/GLCanvas3D.cpp | 26 + src/slic3r/GUI/GLCanvas3D.hpp | 3 + src/slic3r/GUI/MainFrame.cpp | 7 + src/slic3r/GUI/Mouse3DController.cpp | 332 ++++++++ src/slic3r/GUI/Mouse3DController.hpp | 96 +++ src/slic3r/GUI/Plater.cpp | 36 + src/slic3r/GUI/Plater.hpp | 5 + 14 files changed, 3805 insertions(+), 1 deletion(-) create mode 100644 src/hidapi/hidapi.h create mode 100644 src/hidapi/linux/hid.c create mode 100644 src/hidapi/mac/hid.c create mode 100644 src/hidapi/win/hid.c create mode 100644 src/slic3r/GUI/Mouse3DController.cpp create mode 100644 src/slic3r/GUI/Mouse3DController.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c7fc12df..b9a7cf065 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -365,6 +365,25 @@ include_directories(${GLEW_INCLUDE_DIRS}) add_library(cereal INTERFACE) target_include_directories(cereal INTERFACE include) +# Find the hidapi library +if(WIN32) +add_library(hidapi STATIC + ${LIBDIR}/hidapi/win/hid.c +) +elseif (APPLE) +add_library(hidapi STATIC + ${LIBDIR}/hidapi/mac/hid.c +) +else () +add_library(hidapi STATIC + ${LIBDIR}/hidapi/linux/hid.c +) +endif () +set(HIDAPI_FOUND 1) +set(HIDAPI_INCLUDE_DIRS ${LIBDIR}/hidapi/) +set(HIDAPI_LIBRARIES hidapi) +include_directories(${HIDAPI_INCLUDE_DIRS}) + # l10n set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization") add_custom_target(pot diff --git a/src/hidapi/hidapi.h b/src/hidapi/hidapi.h new file mode 100644 index 000000000..1819f8de0 --- /dev/null +++ b/src/hidapi/hidapi.h @@ -0,0 +1,395 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/** @file + * @defgroup API hidapi API + */ + +#ifndef HIDAPI_H__ +#define HIDAPI_H__ + +#include + +#ifdef _WIN32 + #define HID_API_EXPORT __declspec(dllexport) + #define HID_API_CALL +#else + #define HID_API_EXPORT /**< API export macro */ + #define HID_API_CALL /**< API call macro */ +#endif + +#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ + +#ifdef __cplusplus +extern "C" { +#endif + struct hid_device_; + typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ + + /** hidapi info structure */ + struct hid_device_info { + /** Platform-specific device path */ + char *path; + /** Device Vendor ID */ + unsigned short vendor_id; + /** Device Product ID */ + unsigned short product_id; + /** Serial Number */ + wchar_t *serial_number; + /** Device Release Number in binary-coded decimal, + also known as Device Version Number */ + unsigned short release_number; + /** Manufacturer String */ + wchar_t *manufacturer_string; + /** Product string */ + wchar_t *product_string; + /** Usage Page for this Device/Interface + (Windows/Mac only). */ + unsigned short usage_page; + /** Usage for this Device/Interface + (Windows/Mac only).*/ + unsigned short usage; + /** The USB interface which this logical device + represents. + + * Valid on both Linux implementations in all cases. + * Valid on the Windows implementation only if the device + contains more than one interface. + * Valid on the Mac implementation if and only if the device + is a USB HID device. */ + int interface_number; + + /** Pointer to the next device */ + struct hid_device_info *next; + }; + + + /** @brief Initialize the HIDAPI library. + + This function initializes the HIDAPI library. Calling it is not + strictly necessary, as it will be called automatically by + hid_enumerate() and any of the hid_open_*() functions if it is + needed. This function should be called at the beginning of + execution however, if there is a chance of HIDAPI handles + being opened by different threads simultaneously. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_init(void); + + /** @brief Finalize the HIDAPI library. + + This function frees all of the static data associated with + HIDAPI. It should be called at the end of execution to avoid + memory leaks. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_exit(void); + + /** @brief Enumerate the HID Devices. + + This function returns a linked list of all the HID devices + attached to the system which match vendor_id and product_id. + If @p vendor_id is set to 0 then any vendor matches. + If @p product_id is set to 0 then any product matches. + If @p vendor_id and @p product_id are both set to 0, then + all HID devices will be returned. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the types of device + to open. + @param product_id The Product ID (PID) of the types of + device to open. + + @returns + This function returns a pointer to a linked list of type + struct #hid_device_info, containing information about the HID devices + attached to the system, or NULL in the case of failure. Free + this linked list by calling hid_free_enumeration(). + */ + struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); + + /** @brief Free an enumeration Linked List + + This function frees a linked list created by hid_enumerate(). + + @ingroup API + @param devs Pointer to a list of struct_device returned from + hid_enumerate(). + */ + void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); + + /** @brief Open a HID device using a Vendor ID (VID), Product ID + (PID) and optionally a serial number. + + If @p serial_number is NULL, the first device with the + specified VID and PID is opened. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the device to open. + @param product_id The Product ID (PID) of the device to open. + @param serial_number The Serial Number of the device to open + (Optionally NULL). + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number); + + /** @brief Open a HID device by its path name. + + The path name be determined by calling hid_enumerate(), or a + platform-specific path name can be used (eg: /dev/hidraw0 on + Linux). + + @ingroup API + @param path The path name of the device to open + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); + + /** @brief Write an Output report to a HID device. + + The first byte of @p data[] must contain the Report ID. For + devices which only support a single report, this must be set + to 0x0. The remaining bytes contain the report data. Since + the Report ID is mandatory, calls to hid_write() will always + contain one more byte than the report contains. For example, + if a hid report is 16 bytes long, 17 bytes must be passed to + hid_write(), the Report ID (or 0x0, for devices with a + single report), followed by the report data (16 bytes). In + this example, the length passed in would be 17. + + hid_write() will send the data on the first OUT endpoint, if + one exists. If it does not, it will send the data through + the Control Endpoint (Endpoint 0). + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length); + + /** @brief Read an Input report from a HID device with timeout. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + @param milliseconds timeout in milliseconds or -1 for blocking wait. + + @returns + This function returns the actual number of bytes read and + -1 on error. If no packet was available to be read within + the timeout period, this function returns 0. + */ + int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); + + /** @brief Read an Input report from a HID device. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + + @returns + This function returns the actual number of bytes read and + -1 on error. If no packet was available to be read and + the handle is in non-blocking mode, this function returns 0. + */ + int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length); + + /** @brief Set the device handle to be non-blocking. + + In non-blocking mode calls to hid_read() will return + immediately with a value of 0 if there is no data to be + read. In blocking mode, hid_read() will wait (block) until + there is data to read before returning. + + Nonblocking can be turned on and off at any time. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param nonblock enable or not the nonblocking reads + - 1 to enable nonblocking + - 0 to disable nonblocking. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock); + + /** @brief Send a Feature report to the device. + + Feature reports are sent over the Control endpoint as a + Set_Report transfer. The first byte of @p data[] must + contain the Report ID. For devices which only support a + single report, this must be set to 0x0. The remaining bytes + contain the report data. Since the Report ID is mandatory, + calls to hid_send_feature_report() will always contain one + more byte than the report contains. For example, if a hid + report is 16 bytes long, 17 bytes must be passed to + hid_send_feature_report(): the Report ID (or 0x0, for + devices which do not use numbered reports), followed by the + report data (16 bytes). In this example, the length passed + in would be 17. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send, including + the report number. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length); + + /** @brief Get a feature report from a HID device. + + Set the first byte of @p data[] to the Report ID of the + report to be read. Make sure to allow space for this + extra byte in @p data[]. Upon return, the first byte will + still contain the Report ID, and the report data will + start in data[1]. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data A buffer to put the read data into, including + the Report ID. Set the first byte of @p data[] to the + Report ID of the report to be read, or set it to zero + if your device does not use numbered reports. + @param length The number of bytes to read, including an + extra byte for the report ID. The buffer can be longer + than the actual report. + + @returns + This function returns the number of bytes read plus + one for the report ID (which is still in the first + byte), or -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length); + + /** @brief Close a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + */ + void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev); + + /** @brief Get The Manufacturer String from a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen); + + /** @brief Get The Product String from a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen); + + /** @brief Get The Serial Number String from a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen); + + /** @brief Get a string from a HID device, based on its string index. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string_index The index of the string to get. + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen); + + /** @brief Get a string describing the last error which occurred. + + @ingroup API + @param dev A device handle returned from hid_open(). + + @returns + This function returns a string containing the last error + which occurred or NULL if none has occurred. + */ + HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/hidapi/linux/hid.c b/src/hidapi/linux/hid.c new file mode 100644 index 000000000..56dac0fab --- /dev/null +++ b/src/hidapi/linux/hid.c @@ -0,0 +1,797 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + Linux Version - 6/2/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/* C */ +#include +#include +#include +#include +#include + +/* Unix */ +#include +#include +#include +#include +#include +#include +#include + +/* Linux */ +#include +#include +#include +#include + +#include "hidapi.h" + +/* Definitions from linux/hidraw.h. Since these are new, some distros + may not have header files which contain them. */ +#ifndef HIDIOCSFEATURE +#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len) +#endif +#ifndef HIDIOCGFEATURE +#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len) +#endif + + +/* USB HID device property names */ +const char *device_string_names[] = { + "manufacturer", + "product", + "serial", +}; + +/* Symbolic names for the properties above */ +enum device_string_id { + DEVICE_STRING_MANUFACTURER, + DEVICE_STRING_PRODUCT, + DEVICE_STRING_SERIAL, + + DEVICE_STRING_COUNT, +}; + +struct hid_device_ { + int device_handle; + int blocking; + int uses_numbered_reports; +}; + + +static __u32 kernel_version = 0; + +static __u32 detect_kernel_version(void) +{ + struct utsname name; + int major, minor, release; + int ret; + + uname(&name); + ret = sscanf(name.release, "%d.%d.%d", &major, &minor, &release); + if (ret == 3) { + return KERNEL_VERSION(major, minor, release); + } + + ret = sscanf(name.release, "%d.%d", &major, &minor); + if (ret == 2) { + return KERNEL_VERSION(major, minor, 0); + } + + printf("Couldn't determine kernel version from version string \"%s\"\n", name.release); + return 0; +} + +static hid_device *new_hid_device(void) +{ + hid_device *dev = calloc(1, sizeof(hid_device)); + dev->device_handle = -1; + dev->blocking = 1; + dev->uses_numbered_reports = 0; + + return dev; +} + + +/* The caller must free the returned string with free(). */ +static wchar_t *utf8_to_wchar_t(const char *utf8) +{ + wchar_t *ret = NULL; + + if (utf8) { + size_t wlen = mbstowcs(NULL, utf8, 0); + if ((size_t) -1 == wlen) { + return wcsdup(L""); + } + ret = calloc(wlen+1, sizeof(wchar_t)); + mbstowcs(ret, utf8, wlen+1); + ret[wlen] = 0x0000; + } + + return ret; +} + +/* Get an attribute value from a udev_device and return it as a whar_t + string. The returned string must be freed with free() when done.*/ +static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name) +{ + return utf8_to_wchar_t(udev_device_get_sysattr_value(dev, udev_name)); +} + +/* uses_numbered_reports() returns 1 if report_descriptor describes a device + which contains numbered reports. */ +static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) { + unsigned int i = 0; + int size_code; + int data_len, key_size; + + while (i < size) { + int key = report_descriptor[i]; + + /* Check for the Report ID key */ + if (key == 0x85/*Report ID*/) { + /* This device has a Report ID, which means it uses + numbered reports. */ + return 1; + } + + //printf("key: %02hhx\n", key); + + if ((key & 0xf0) == 0xf0) { + /* This is a Long Item. The next byte contains the + length of the data section (value) for this key. + See the HID specification, version 1.11, section + 6.2.2.3, titled "Long Items." */ + if (i+1 < size) + data_len = report_descriptor[i+1]; + else + data_len = 0; /* malformed report */ + key_size = 3; + } + else { + /* This is a Short Item. The bottom two bits of the + key contain the size code for the data section + (value) for this key. Refer to the HID + specification, version 1.11, section 6.2.2.2, + titled "Short Items." */ + size_code = key & 0x3; + switch (size_code) { + case 0: + case 1: + case 2: + data_len = size_code; + break; + case 3: + data_len = 4; + break; + default: + /* Can't ever happen since size_code is & 0x3 */ + data_len = 0; + break; + }; + key_size = 1; + } + + /* Skip over this key and it's associated data */ + i += data_len + key_size; + } + + /* Didn't find a Report ID key. Device doesn't use numbered reports. */ + return 0; +} + +/* + * The caller is responsible for free()ing the (newly-allocated) character + * strings pointed to by serial_number_utf8 and product_name_utf8 after use. + */ +static int +parse_uevent_info(const char *uevent, int *bus_type, + unsigned short *vendor_id, unsigned short *product_id, + char **serial_number_utf8, char **product_name_utf8) +{ + char *tmp = strdup(uevent); + char *saveptr = NULL; + char *line; + char *key; + char *value; + + int found_id = 0; + int found_serial = 0; + int found_name = 0; + + line = strtok_r(tmp, "\n", &saveptr); + while (line != NULL) { + /* line: "KEY=value" */ + key = line; + value = strchr(line, '='); + if (!value) { + goto next_line; + } + *value = '\0'; + value++; + + if (strcmp(key, "HID_ID") == 0) { + /** + * type vendor product + * HID_ID=0003:000005AC:00008242 + **/ + int ret = sscanf(value, "%x:%hx:%hx", bus_type, vendor_id, product_id); + if (ret == 3) { + found_id = 1; + } + } else if (strcmp(key, "HID_NAME") == 0) { + /* The caller has to free the product name */ + *product_name_utf8 = strdup(value); + found_name = 1; + } else if (strcmp(key, "HID_UNIQ") == 0) { + /* The caller has to free the serial number */ + *serial_number_utf8 = strdup(value); + found_serial = 1; + } + +next_line: + line = strtok_r(NULL, "\n", &saveptr); + } + + free(tmp); + return (found_id && found_name && found_serial); +} + + +static int get_device_string(hid_device *dev, enum device_string_id key, wchar_t *string, size_t maxlen) +{ + struct udev *udev; + struct udev_device *udev_dev, *parent, *hid_dev; + struct stat s; + int ret = -1; + char *serial_number_utf8 = NULL; + char *product_name_utf8 = NULL; + + /* Create the udev object */ + udev = udev_new(); + if (!udev) { + printf("Can't create udev\n"); + return -1; + } + + /* Get the dev_t (major/minor numbers) from the file handle. */ + ret = fstat(dev->device_handle, &s); + if (-1 == ret) + return ret; + /* Open a udev device from the dev_t. 'c' means character device. */ + udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev); + if (udev_dev) { + hid_dev = udev_device_get_parent_with_subsystem_devtype( + udev_dev, + "hid", + NULL); + if (hid_dev) { + unsigned short dev_vid; + unsigned short dev_pid; + int bus_type; + size_t retm; + + ret = parse_uevent_info( + udev_device_get_sysattr_value(hid_dev, "uevent"), + &bus_type, + &dev_vid, + &dev_pid, + &serial_number_utf8, + &product_name_utf8); + + if (bus_type == BUS_BLUETOOTH) { + switch (key) { + case DEVICE_STRING_MANUFACTURER: + wcsncpy(string, L"", maxlen); + ret = 0; + break; + case DEVICE_STRING_PRODUCT: + retm = mbstowcs(string, product_name_utf8, maxlen); + ret = (retm == (size_t)-1)? -1: 0; + break; + case DEVICE_STRING_SERIAL: + retm = mbstowcs(string, serial_number_utf8, maxlen); + ret = (retm == (size_t)-1)? -1: 0; + break; + case DEVICE_STRING_COUNT: + default: + ret = -1; + break; + } + } + else { + /* This is a USB device. Find its parent USB Device node. */ + parent = udev_device_get_parent_with_subsystem_devtype( + udev_dev, + "usb", + "usb_device"); + if (parent) { + const char *str; + const char *key_str = NULL; + + if (key >= 0 && key < DEVICE_STRING_COUNT) { + key_str = device_string_names[key]; + } else { + ret = -1; + goto end; + } + + str = udev_device_get_sysattr_value(parent, key_str); + if (str) { + /* Convert the string from UTF-8 to wchar_t */ + retm = mbstowcs(string, str, maxlen); + ret = (retm == (size_t)-1)? -1: 0; + goto end; + } + } + } + } + } + +end: + free(serial_number_utf8); + free(product_name_utf8); + + udev_device_unref(udev_dev); + /* parent and hid_dev don't need to be (and can't be) unref'd. + I'm not sure why, but they'll throw double-free() errors. */ + udev_unref(udev); + + return ret; +} + +int HID_API_EXPORT hid_init(void) +{ + const char *locale; + + /* Set the locale if it's not set. */ + locale = setlocale(LC_CTYPE, NULL); + if (!locale) + setlocale(LC_CTYPE, ""); + + kernel_version = detect_kernel_version(); + + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ + /* Nothing to do for this in the Linux/hidraw implementation. */ + return 0; +} + + +struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + struct udev *udev; + struct udev_enumerate *enumerate; + struct udev_list_entry *devices, *dev_list_entry; + + struct hid_device_info *root = NULL; /* return object */ + struct hid_device_info *cur_dev = NULL; + struct hid_device_info *prev_dev = NULL; /* previous device */ + + hid_init(); + + /* Create the udev object */ + udev = udev_new(); + if (!udev) { + printf("Can't create udev\n"); + return NULL; + } + + /* Create a list of the devices in the 'hidraw' subsystem. */ + enumerate = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(enumerate, "hidraw"); + udev_enumerate_scan_devices(enumerate); + devices = udev_enumerate_get_list_entry(enumerate); + /* For each item, see if it matches the vid/pid, and if so + create a udev_device record for it */ + udev_list_entry_foreach(dev_list_entry, devices) { + const char *sysfs_path; + const char *dev_path; + const char *str; + struct udev_device *raw_dev; /* The device's hidraw udev node. */ + struct udev_device *hid_dev; /* The device's HID udev node. */ + struct udev_device *usb_dev; /* The device's USB udev node. */ + struct udev_device *intf_dev; /* The device's interface (in the USB sense). */ + unsigned short dev_vid; + unsigned short dev_pid; + char *serial_number_utf8 = NULL; + char *product_name_utf8 = NULL; + int bus_type; + int result; + + /* Get the filename of the /sys entry for the device + and create a udev_device object (dev) representing it */ + sysfs_path = udev_list_entry_get_name(dev_list_entry); + raw_dev = udev_device_new_from_syspath(udev, sysfs_path); + dev_path = udev_device_get_devnode(raw_dev); + + hid_dev = udev_device_get_parent_with_subsystem_devtype( + raw_dev, + "hid", + NULL); + + if (!hid_dev) { + /* Unable to find parent hid device. */ + goto next; + } + + result = parse_uevent_info( + udev_device_get_sysattr_value(hid_dev, "uevent"), + &bus_type, + &dev_vid, + &dev_pid, + &serial_number_utf8, + &product_name_utf8); + + if (!result) { + /* parse_uevent_info() failed for at least one field. */ + goto next; + } + + if (bus_type != BUS_USB && bus_type != BUS_BLUETOOTH) { + /* We only know how to handle USB and BT devices. */ + goto next; + } + + /* Check the VID/PID against the arguments */ + if ((vendor_id == 0x0 || vendor_id == dev_vid) && + (product_id == 0x0 || product_id == dev_pid)) { + struct hid_device_info *tmp; + + /* VID/PID match. Create the record. */ + tmp = malloc(sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + prev_dev = cur_dev; + cur_dev = tmp; + + /* Fill out the record */ + cur_dev->next = NULL; + cur_dev->path = dev_path? strdup(dev_path): NULL; + + /* VID/PID */ + cur_dev->vendor_id = dev_vid; + cur_dev->product_id = dev_pid; + + /* Serial Number */ + cur_dev->serial_number = utf8_to_wchar_t(serial_number_utf8); + + /* Release Number */ + cur_dev->release_number = 0x0; + + /* Interface Number */ + cur_dev->interface_number = -1; + + switch (bus_type) { + case BUS_USB: + /* The device pointed to by raw_dev contains information about + the hidraw device. In order to get information about the + USB device, get the parent device with the + subsystem/devtype pair of "usb"/"usb_device". This will + be several levels up the tree, but the function will find + it. */ + usb_dev = udev_device_get_parent_with_subsystem_devtype( + raw_dev, + "usb", + "usb_device"); + + if (!usb_dev) { + /* Free this device */ + free(cur_dev->serial_number); + free(cur_dev->path); + free(cur_dev); + + /* Take it off the device list. */ + if (prev_dev) { + prev_dev->next = NULL; + cur_dev = prev_dev; + } + else { + cur_dev = root = NULL; + } + + goto next; + } + + /* Manufacturer and Product strings */ + cur_dev->manufacturer_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_MANUFACTURER]); + cur_dev->product_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_PRODUCT]); + + /* Release Number */ + str = udev_device_get_sysattr_value(usb_dev, "bcdDevice"); + cur_dev->release_number = (str)? strtol(str, NULL, 16): 0x0; + + /* Get a handle to the interface's udev node. */ + intf_dev = udev_device_get_parent_with_subsystem_devtype( + raw_dev, + "usb", + "usb_interface"); + if (intf_dev) { + str = udev_device_get_sysattr_value(intf_dev, "bInterfaceNumber"); + cur_dev->interface_number = (str)? strtol(str, NULL, 16): -1; + } + + break; + + case BUS_BLUETOOTH: + /* Manufacturer and Product strings */ + cur_dev->manufacturer_string = wcsdup(L""); + cur_dev->product_string = utf8_to_wchar_t(product_name_utf8); + + break; + + default: + /* Unknown device type - this should never happen, as we + * check for USB and Bluetooth devices above */ + break; + } + } + + next: + free(serial_number_utf8); + free(product_name_utf8); + udev_device_unref(raw_dev); + /* hid_dev, usb_dev and intf_dev don't need to be (and can't be) + unref()d. It will cause a double-free() error. I'm not + sure why. */ + } + /* Free the enumerator and udev objects. */ + udev_enumerate_unref(enumerate); + udev_unref(udev); + + return root; +} + +void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) +{ + struct hid_device_info *d = devs; + while (d) { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + +hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) +{ + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + devs = hid_enumerate(vendor_id, product_id); + cur_dev = devs; + while (cur_dev) { + if (cur_dev->vendor_id == vendor_id && + cur_dev->product_id == product_id) { + if (serial_number) { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) { + path_to_open = cur_dev->path; + break; + } + } + else { + path_to_open = cur_dev->path; + break; + } + } + cur_dev = cur_dev->next; + } + + if (path_to_open) { + /* Open the device */ + handle = hid_open_path(path_to_open); + } + + hid_free_enumeration(devs); + + return handle; +} + +hid_device * HID_API_EXPORT hid_open_path(const char *path) +{ + hid_device *dev = NULL; + + hid_init(); + + dev = new_hid_device(); + + /* OPEN HERE */ + dev->device_handle = open(path, O_RDWR); + + /* If we have a good handle, return it. */ + if (dev->device_handle > 0) { + + /* Get the report descriptor */ + int res, desc_size = 0; + struct hidraw_report_descriptor rpt_desc; + + memset(&rpt_desc, 0x0, sizeof(rpt_desc)); + + /* Get Report Descriptor Size */ + res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size); + if (res < 0) + perror("HIDIOCGRDESCSIZE"); + + + /* Get Report Descriptor */ + rpt_desc.size = desc_size; + res = ioctl(dev->device_handle, HIDIOCGRDESC, &rpt_desc); + if (res < 0) { + perror("HIDIOCGRDESC"); + } else { + /* Determine if this device uses numbered reports. */ + dev->uses_numbered_reports = + uses_numbered_reports(rpt_desc.value, + rpt_desc.size); + } + + return dev; + } + else { + /* Unable to open any devices. */ + free(dev); + return NULL; + } +} + + +int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + int bytes_written; + + bytes_written = write(dev->device_handle, data, length); + + return bytes_written; +} + + +int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + int bytes_read; + + if (milliseconds >= 0) { + /* Milliseconds is either 0 (non-blocking) or > 0 (contains + a valid timeout). In both cases we want to call poll() + and wait for data to arrive. Don't rely on non-blocking + operation (O_NONBLOCK) since some kernels don't seem to + properly report device disconnection through read() when + in non-blocking mode. */ + int ret; + struct pollfd fds; + + fds.fd = dev->device_handle; + fds.events = POLLIN; + fds.revents = 0; + ret = poll(&fds, 1, milliseconds); + if (ret == -1 || ret == 0) { + /* Error or timeout */ + return ret; + } + else { + /* Check for errors on the file descriptor. This will + indicate a device disconnection. */ + if (fds.revents & (POLLERR | POLLHUP | POLLNVAL)) + return -1; + } + } + + bytes_read = read(dev->device_handle, data, length); + if (bytes_read < 0 && (errno == EAGAIN || errno == EINPROGRESS)) + bytes_read = 0; + + if (bytes_read >= 0 && + kernel_version != 0 && + kernel_version < KERNEL_VERSION(2,6,34) && + dev->uses_numbered_reports) { + /* Work around a kernel bug. Chop off the first byte. */ + memmove(data, data+1, bytes_read); + bytes_read--; + } + + return bytes_read; +} + +int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) +{ + return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); +} + +int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) +{ + /* Do all non-blocking in userspace using poll(), since it looks + like there's a bug in the kernel in some versions where + read() will not return -1 on disconnection of the USB device */ + + dev->blocking = !nonblock; + return 0; /* Success */ +} + + +int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + int res; + + res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data); + if (res < 0) + perror("ioctl (SFEATURE)"); + + return res; +} + +int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + int res; + + res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data); + if (res < 0) + perror("ioctl (GFEATURE)"); + + + return res; +} + + +void HID_API_EXPORT hid_close(hid_device *dev) +{ + if (!dev) + return; + close(dev->device_handle); + free(dev); +} + + +int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_device_string(dev, DEVICE_STRING_MANUFACTURER, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_device_string(dev, DEVICE_STRING_PRODUCT, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_device_string(dev, DEVICE_STRING_SERIAL, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + return -1; +} + + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) +{ + return NULL; +} diff --git a/src/hidapi/mac/hid.c b/src/hidapi/mac/hid.c new file mode 100644 index 000000000..ca10a9cca --- /dev/null +++ b/src/hidapi/mac/hid.c @@ -0,0 +1,1121 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 2010-07-03 + + Copyright 2010, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/* See Apple Technical Note TN2187 for details on IOHidManager. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hidapi.h" + +/* Barrier implementation because Mac OSX doesn't have pthread_barrier. + It also doesn't have clock_gettime(). So much for POSIX and SUSv2. + This implementation came from Brent Priddy and was posted on + StackOverflow. It is used with his permission. */ +typedef int pthread_barrierattr_t; +typedef struct pthread_barrier { + pthread_mutex_t mutex; + pthread_cond_t cond; + int count; + int trip_count; +} pthread_barrier_t; + +static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) +{ + if(count == 0) { + errno = EINVAL; + return -1; + } + + if(pthread_mutex_init(&barrier->mutex, 0) < 0) { + return -1; + } + if(pthread_cond_init(&barrier->cond, 0) < 0) { + pthread_mutex_destroy(&barrier->mutex); + return -1; + } + barrier->trip_count = count; + barrier->count = 0; + + return 0; +} + +static int pthread_barrier_destroy(pthread_barrier_t *barrier) +{ + pthread_cond_destroy(&barrier->cond); + pthread_mutex_destroy(&barrier->mutex); + return 0; +} + +static int pthread_barrier_wait(pthread_barrier_t *barrier) +{ + pthread_mutex_lock(&barrier->mutex); + ++(barrier->count); + if(barrier->count >= barrier->trip_count) + { + barrier->count = 0; + pthread_cond_broadcast(&barrier->cond); + pthread_mutex_unlock(&barrier->mutex); + return 1; + } + else + { + pthread_cond_wait(&barrier->cond, &(barrier->mutex)); + pthread_mutex_unlock(&barrier->mutex); + return 0; + } +} + +static int return_data(hid_device *dev, unsigned char *data, size_t length); + +/* Linked List of input reports received from the device. */ +struct input_report { + uint8_t *data; + size_t len; + struct input_report *next; +}; + +struct hid_device_ { + IOHIDDeviceRef device_handle; + int blocking; + int uses_numbered_reports; + int disconnected; + CFStringRef run_loop_mode; + CFRunLoopRef run_loop; + CFRunLoopSourceRef source; + uint8_t *input_report_buf; + CFIndex max_input_report_len; + struct input_report *input_reports; + + pthread_t thread; + pthread_mutex_t mutex; /* Protects input_reports */ + pthread_cond_t condition; + pthread_barrier_t barrier; /* Ensures correct startup sequence */ + pthread_barrier_t shutdown_barrier; /* Ensures correct shutdown sequence */ + int shutdown_thread; +}; + +static hid_device *new_hid_device(void) +{ + hid_device *dev = calloc(1, sizeof(hid_device)); + dev->device_handle = NULL; + dev->blocking = 1; + dev->uses_numbered_reports = 0; + dev->disconnected = 0; + dev->run_loop_mode = NULL; + dev->run_loop = NULL; + dev->source = NULL; + dev->input_report_buf = NULL; + dev->input_reports = NULL; + dev->shutdown_thread = 0; + + /* Thread objects */ + pthread_mutex_init(&dev->mutex, NULL); + pthread_cond_init(&dev->condition, NULL); + pthread_barrier_init(&dev->barrier, NULL, 2); + pthread_barrier_init(&dev->shutdown_barrier, NULL, 2); + + return dev; +} + +static void free_hid_device(hid_device *dev) +{ + if (!dev) + return; + + /* Delete any input reports still left over. */ + struct input_report *rpt = dev->input_reports; + while (rpt) { + struct input_report *next = rpt->next; + free(rpt->data); + free(rpt); + rpt = next; + } + + /* Free the string and the report buffer. The check for NULL + is necessary here as CFRelease() doesn't handle NULL like + free() and others do. */ + if (dev->run_loop_mode) + CFRelease(dev->run_loop_mode); + if (dev->source) + CFRelease(dev->source); + free(dev->input_report_buf); + + /* Clean up the thread objects */ + pthread_barrier_destroy(&dev->shutdown_barrier); + pthread_barrier_destroy(&dev->barrier); + pthread_cond_destroy(&dev->condition); + pthread_mutex_destroy(&dev->mutex); + + /* Free the structure itself. */ + free(dev); +} + +static IOHIDManagerRef hid_mgr = 0x0; + + +#if 0 +static void register_error(hid_device *dev, const char *op) +{ + +} +#endif + + +static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key) +{ + CFTypeRef ref; + int32_t value; + + ref = IOHIDDeviceGetProperty(device, key); + if (ref) { + if (CFGetTypeID(ref) == CFNumberGetTypeID()) { + CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &value); + return value; + } + } + return 0; +} + +static unsigned short get_vendor_id(IOHIDDeviceRef device) +{ + return get_int_property(device, CFSTR(kIOHIDVendorIDKey)); +} + +static unsigned short get_product_id(IOHIDDeviceRef device) +{ + return get_int_property(device, CFSTR(kIOHIDProductIDKey)); +} + +static int32_t get_max_report_length(IOHIDDeviceRef device) +{ + return get_int_property(device, CFSTR(kIOHIDMaxInputReportSizeKey)); +} + +static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t *buf, size_t len) +{ + CFStringRef str; + + if (!len) + return 0; + + str = IOHIDDeviceGetProperty(device, prop); + + buf[0] = 0; + + if (str) { + CFIndex str_len = CFStringGetLength(str); + CFRange range; + CFIndex used_buf_len; + CFIndex chars_copied; + + len --; + + range.location = 0; + range.length = ((size_t)str_len > len)? len: (size_t)str_len; + chars_copied = CFStringGetBytes(str, + range, + kCFStringEncodingUTF32LE, + (char)'?', + FALSE, + (UInt8*)buf, + len * sizeof(wchar_t), + &used_buf_len); + + if (chars_copied == len) + buf[len] = 0; /* len is decremented above */ + else + buf[chars_copied] = 0; + + return 0; + } + else + return -1; + +} + +static int get_serial_number(IOHIDDeviceRef device, wchar_t *buf, size_t len) +{ + return get_string_property(device, CFSTR(kIOHIDSerialNumberKey), buf, len); +} + +static int get_manufacturer_string(IOHIDDeviceRef device, wchar_t *buf, size_t len) +{ + return get_string_property(device, CFSTR(kIOHIDManufacturerKey), buf, len); +} + +static int get_product_string(IOHIDDeviceRef device, wchar_t *buf, size_t len) +{ + return get_string_property(device, CFSTR(kIOHIDProductKey), buf, len); +} + + +/* Implementation of wcsdup() for Mac. */ +static wchar_t *dup_wcs(const wchar_t *s) +{ + size_t len = wcslen(s); + wchar_t *ret = malloc((len+1)*sizeof(wchar_t)); + wcscpy(ret, s); + + return ret; +} + +/* hidapi_IOHIDDeviceGetService() + * + * Return the io_service_t corresponding to a given IOHIDDeviceRef, either by: + * - on OS X 10.6 and above, calling IOHIDDeviceGetService() + * - on OS X 10.5, extract it from the IOHIDDevice struct + */ +static io_service_t hidapi_IOHIDDeviceGetService(IOHIDDeviceRef device) +{ + static void *iokit_framework = NULL; + static io_service_t (*dynamic_IOHIDDeviceGetService)(IOHIDDeviceRef device) = NULL; + + /* Use dlopen()/dlsym() to get a pointer to IOHIDDeviceGetService() if it exists. + * If any of these steps fail, dynamic_IOHIDDeviceGetService will be left NULL + * and the fallback method will be used. + */ + if (iokit_framework == NULL) { + iokit_framework = dlopen("/System/Library/IOKit.framework/IOKit", RTLD_LAZY); + + if (iokit_framework != NULL) + dynamic_IOHIDDeviceGetService = dlsym(iokit_framework, "IOHIDDeviceGetService"); + } + + if (dynamic_IOHIDDeviceGetService != NULL) { + /* Running on OS X 10.6 and above: IOHIDDeviceGetService() exists */ + return dynamic_IOHIDDeviceGetService(device); + } + else + { + /* Running on OS X 10.5: IOHIDDeviceGetService() doesn't exist. + * + * Be naughty and pull the service out of the IOHIDDevice. + * IOHIDDevice is an opaque struct not exposed to applications, but its + * layout is stable through all available versions of OS X. + * Tested and working on OS X 10.5.8 i386, x86_64, and ppc. + */ + struct IOHIDDevice_internal { + /* The first field of the IOHIDDevice struct is a + * CFRuntimeBase (which is a private CF struct). + * + * a, b, and c are the 3 fields that make up a CFRuntimeBase. + * See http://opensource.apple.com/source/CF/CF-476.18/CFRuntime.h + * + * The second field of the IOHIDDevice is the io_service_t we're looking for. + */ + uintptr_t a; + uint8_t b[4]; +#if __LP64__ + uint32_t c; +#endif + io_service_t service; + }; + struct IOHIDDevice_internal *tmp = (struct IOHIDDevice_internal *)device; + + return tmp->service; + } +} + +/* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */ +static int init_hid_manager(void) +{ + /* Initialize all the HID Manager Objects */ + hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + if (hid_mgr) { + IOHIDManagerSetDeviceMatching(hid_mgr, NULL); + IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + return 0; + } + + return -1; +} + +/* Initialize the IOHIDManager if necessary. This is the public function, and + it is safe to call this function repeatedly. Return 0 for success and -1 + for failure. */ +int HID_API_EXPORT hid_init(void) +{ + if (!hid_mgr) { + return init_hid_manager(); + } + + /* Already initialized. */ + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ + if (hid_mgr) { + /* Close the HID manager. */ + IOHIDManagerClose(hid_mgr, kIOHIDOptionsTypeNone); + CFRelease(hid_mgr); + hid_mgr = NULL; + } + + return 0; +} + +static void process_pending_events(void) { + SInt32 res; + do { + res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, FALSE); + } while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut); +} + +struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + struct hid_device_info *root = NULL; /* return object */ + struct hid_device_info *cur_dev = NULL; + CFIndex num_devices; + int i; + + /* Set up the HID Manager if it hasn't been done */ + if (hid_init() < 0) + return NULL; + + /* give the IOHIDManager a chance to update itself */ + process_pending_events(); + + /* Get a list of the Devices */ + IOHIDManagerSetDeviceMatching(hid_mgr, NULL); + CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr); + + /* Convert the list into a C array so we can iterate easily. */ + num_devices = CFSetGetCount(device_set); + IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); + CFSetGetValues(device_set, (const void **) device_array); + + /* Iterate over each device, making an entry for it. */ + for (i = 0; i < num_devices; i++) { + unsigned short dev_vid; + unsigned short dev_pid; + #define BUF_LEN 256 + wchar_t buf[BUF_LEN]; + + IOHIDDeviceRef dev = device_array[i]; + + if (!dev) { + continue; + } + dev_vid = get_vendor_id(dev); + dev_pid = get_product_id(dev); + + /* Check the VID/PID against the arguments */ + if ((vendor_id == 0x0 || vendor_id == dev_vid) && + (product_id == 0x0 || product_id == dev_pid)) { + struct hid_device_info *tmp; + bool is_usb_hid; /* Is this an actual HID usb device */ + io_object_t iokit_dev; + kern_return_t res; + io_string_t path; + + /* VID/PID match. Create the record. */ + tmp = malloc(sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; + + is_usb_hid = get_int_property(dev, CFSTR(kUSBInterfaceClass)) == kUSBHIDClass; + + /* Get the Usage Page and Usage for this device. */ + cur_dev->usage_page = get_int_property(dev, CFSTR(kIOHIDPrimaryUsagePageKey)); + cur_dev->usage = get_int_property(dev, CFSTR(kIOHIDPrimaryUsageKey)); + + /* Fill out the record */ + cur_dev->next = NULL; + + /* Fill in the path (IOService plane) */ + iokit_dev = hidapi_IOHIDDeviceGetService(dev); + res = IORegistryEntryGetPath(iokit_dev, kIOServicePlane, path); + if (res == KERN_SUCCESS) + cur_dev->path = strdup(path); + else + cur_dev->path = strdup(""); + + /* Serial Number */ + get_serial_number(dev, buf, BUF_LEN); + cur_dev->serial_number = dup_wcs(buf); + + /* Manufacturer and Product strings */ + get_manufacturer_string(dev, buf, BUF_LEN); + cur_dev->manufacturer_string = dup_wcs(buf); + get_product_string(dev, buf, BUF_LEN); + cur_dev->product_string = dup_wcs(buf); + + /* VID/PID */ + cur_dev->vendor_id = dev_vid; + cur_dev->product_id = dev_pid; + + /* Release Number */ + cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey)); + + /* We can only retrieve the interface number for USB HID devices. + * IOKit always seems to return 0 when querying a standard USB device + * for its interface. */ + if (is_usb_hid) { + /* Get the interface number */ + cur_dev->interface_number = get_int_property(dev, CFSTR(kUSBInterfaceNumber)); + } else { + cur_dev->interface_number = -1; + } + } + } + + free(device_array); + CFRelease(device_set); + + return root; +} + +void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) +{ + /* This function is identical to the Linux version. Platform independent. */ + struct hid_device_info *d = devs; + while (d) { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + +hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) +{ + /* This function is identical to the Linux version. Platform independent. */ + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device * handle = NULL; + + devs = hid_enumerate(vendor_id, product_id); + cur_dev = devs; + while (cur_dev) { + if (cur_dev->vendor_id == vendor_id && + cur_dev->product_id == product_id) { + if (serial_number) { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) { + path_to_open = cur_dev->path; + break; + } + } + else { + path_to_open = cur_dev->path; + break; + } + } + cur_dev = cur_dev->next; + } + + if (path_to_open) { + /* Open the device */ + handle = hid_open_path(path_to_open); + } + + hid_free_enumeration(devs); + + return handle; +} + +static void hid_device_removal_callback(void *context, IOReturn result, + void *sender) +{ + /* Stop the Run Loop for this device. */ + hid_device *d = context; + + d->disconnected = 1; + CFRunLoopStop(d->run_loop); +} + +/* The Run Loop calls this function for each input report received. + This function puts the data into a linked list to be picked up by + hid_read(). */ +static void hid_report_callback(void *context, IOReturn result, void *sender, + IOHIDReportType report_type, uint32_t report_id, + uint8_t *report, CFIndex report_length) +{ + struct input_report *rpt; + hid_device *dev = context; + + /* Make a new Input Report object */ + rpt = calloc(1, sizeof(struct input_report)); + rpt->data = calloc(1, report_length); + memcpy(rpt->data, report, report_length); + rpt->len = report_length; + rpt->next = NULL; + + /* Lock this section */ + pthread_mutex_lock(&dev->mutex); + + /* Attach the new report object to the end of the list. */ + if (dev->input_reports == NULL) { + /* The list is empty. Put it at the root. */ + dev->input_reports = rpt; + } + else { + /* Find the end of the list and attach. */ + struct input_report *cur = dev->input_reports; + int num_queued = 0; + while (cur->next != NULL) { + cur = cur->next; + num_queued++; + } + cur->next = rpt; + + /* Pop one off if we've reached 30 in the queue. This + way we don't grow forever if the user never reads + anything from the device. */ + if (num_queued > 30) { + return_data(dev, NULL, 0); + } + } + + /* Signal a waiting thread that there is data. */ + pthread_cond_signal(&dev->condition); + + /* Unlock */ + pthread_mutex_unlock(&dev->mutex); + +} + +/* This gets called when the read_thread's run loop gets signaled by + hid_close(), and serves to stop the read_thread's run loop. */ +static void perform_signal_callback(void *context) +{ + hid_device *dev = context; + CFRunLoopStop(dev->run_loop); /*TODO: CFRunLoopGetCurrent()*/ +} + +static void *read_thread(void *param) +{ + hid_device *dev = param; + SInt32 code; + + /* Move the device's run loop to this thread. */ + IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetCurrent(), dev->run_loop_mode); + + /* Create the RunLoopSource which is used to signal the + event loop to stop when hid_close() is called. */ + CFRunLoopSourceContext ctx; + memset(&ctx, 0, sizeof(ctx)); + ctx.version = 0; + ctx.info = dev; + ctx.perform = &perform_signal_callback; + dev->source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0/*order*/, &ctx); + CFRunLoopAddSource(CFRunLoopGetCurrent(), dev->source, dev->run_loop_mode); + + /* Store off the Run Loop so it can be stopped from hid_close() + and on device disconnection. */ + dev->run_loop = CFRunLoopGetCurrent(); + + /* Notify the main thread that the read thread is up and running. */ + pthread_barrier_wait(&dev->barrier); + + /* Run the Event Loop. CFRunLoopRunInMode() will dispatch HID input + reports into the hid_report_callback(). */ + while (!dev->shutdown_thread && !dev->disconnected) { + code = CFRunLoopRunInMode(dev->run_loop_mode, 1000/*sec*/, FALSE); + /* Return if the device has been disconnected */ + if (code == kCFRunLoopRunFinished) { + dev->disconnected = 1; + break; + } + + + /* Break if The Run Loop returns Finished or Stopped. */ + if (code != kCFRunLoopRunTimedOut && + code != kCFRunLoopRunHandledSource) { + /* There was some kind of error. Setting + shutdown seems to make sense, but + there may be something else more appropriate */ + dev->shutdown_thread = 1; + break; + } + } + + /* Now that the read thread is stopping, Wake any threads which are + waiting on data (in hid_read_timeout()). Do this under a mutex to + make sure that a thread which is about to go to sleep waiting on + the condition actually will go to sleep before the condition is + signaled. */ + pthread_mutex_lock(&dev->mutex); + pthread_cond_broadcast(&dev->condition); + pthread_mutex_unlock(&dev->mutex); + + /* Wait here until hid_close() is called and makes it past + the call to CFRunLoopWakeUp(). This thread still needs to + be valid when that function is called on the other thread. */ + pthread_barrier_wait(&dev->shutdown_barrier); + + return NULL; +} + +/* hid_open_path() + * + * path must be a valid path to an IOHIDDevice in the IOService plane + * Example: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver" + */ +hid_device * HID_API_EXPORT hid_open_path(const char *path) +{ + hid_device *dev = NULL; + io_registry_entry_t entry = MACH_PORT_NULL; + + dev = new_hid_device(); + + /* Set up the HID Manager if it hasn't been done */ + if (hid_init() < 0) + return NULL; + + /* Get the IORegistry entry for the given path */ + entry = IORegistryEntryFromPath(kIOMasterPortDefault, path); + if (entry == MACH_PORT_NULL) { + /* Path wasn't valid (maybe device was removed?) */ + goto return_error; + } + + /* Create an IOHIDDevice for the entry */ + dev->device_handle = IOHIDDeviceCreate(kCFAllocatorDefault, entry); + if (dev->device_handle == NULL) { + /* Error creating the HID device */ + goto return_error; + } + + /* Open the IOHIDDevice */ + IOReturn ret = IOHIDDeviceOpen(dev->device_handle, kIOHIDOptionsTypeSeizeDevice); + if (ret == kIOReturnSuccess) { + char str[32]; + + /* Create the buffers for receiving data */ + dev->max_input_report_len = (CFIndex) get_max_report_length(dev->device_handle); + dev->input_report_buf = calloc(dev->max_input_report_len, sizeof(uint8_t)); + + /* Create the Run Loop Mode for this device. + printing the reference seems to work. */ + sprintf(str, "HIDAPI_%p", dev->device_handle); + dev->run_loop_mode = + CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII); + + /* Attach the device to a Run Loop */ + IOHIDDeviceRegisterInputReportCallback( + dev->device_handle, dev->input_report_buf, dev->max_input_report_len, + &hid_report_callback, dev); + IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev); + + /* Start the read thread */ + pthread_create(&dev->thread, NULL, read_thread, dev); + + /* Wait here for the read thread to be initialized. */ + pthread_barrier_wait(&dev->barrier); + + IOObjectRelease(entry); + return dev; + } + else { + goto return_error; + } + +return_error: + if (dev->device_handle != NULL) + CFRelease(dev->device_handle); + + if (entry != MACH_PORT_NULL) + IOObjectRelease(entry); + + free_hid_device(dev); + return NULL; +} + +static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char *data, size_t length) +{ + const unsigned char *data_to_send; + size_t length_to_send; + IOReturn res; + + /* Return if the device has been disconnected. */ + if (dev->disconnected) + return -1; + + if (data[0] == 0x0) { + /* Not using numbered Reports. + Don't send the report number. */ + data_to_send = data+1; + length_to_send = length-1; + } + else { + /* Using numbered Reports. + Send the Report Number */ + data_to_send = data; + length_to_send = length; + } + + if (!dev->disconnected) { + res = IOHIDDeviceSetReport(dev->device_handle, + type, + data[0], /* Report ID*/ + data_to_send, length_to_send); + + if (res == kIOReturnSuccess) { + return length; + } + else + return -1; + } + + return -1; +} + +int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + return set_report(dev, kIOHIDReportTypeOutput, data, length); +} + +/* Helper function, so that this isn't duplicated in hid_read(). */ +static int return_data(hid_device *dev, unsigned char *data, size_t length) +{ + /* Copy the data out of the linked list item (rpt) into the + return buffer (data), and delete the liked list item. */ + struct input_report *rpt = dev->input_reports; + size_t len = (length < rpt->len)? length: rpt->len; + memcpy(data, rpt->data, len); + dev->input_reports = rpt->next; + free(rpt->data); + free(rpt); + return len; +} + +static int cond_wait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + while (!dev->input_reports) { + int res = pthread_cond_wait(cond, mutex); + if (res != 0) + return res; + + /* A res of 0 means we may have been signaled or it may + be a spurious wakeup. Check to see that there's acutally + data in the queue before returning, and if not, go back + to sleep. See the pthread_cond_timedwait() man page for + details. */ + + if (dev->shutdown_thread || dev->disconnected) + return -1; + } + + return 0; +} + +static int cond_timedwait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) +{ + while (!dev->input_reports) { + int res = pthread_cond_timedwait(cond, mutex, abstime); + if (res != 0) + return res; + + /* A res of 0 means we may have been signaled or it may + be a spurious wakeup. Check to see that there's acutally + data in the queue before returning, and if not, go back + to sleep. See the pthread_cond_timedwait() man page for + details. */ + + if (dev->shutdown_thread || dev->disconnected) + return -1; + } + + return 0; + +} + +int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + int bytes_read = -1; + + /* Lock the access to the report list. */ + pthread_mutex_lock(&dev->mutex); + + /* There's an input report queued up. Return it. */ + if (dev->input_reports) { + /* Return the first one */ + bytes_read = return_data(dev, data, length); + goto ret; + } + + /* Return if the device has been disconnected. */ + if (dev->disconnected) { + bytes_read = -1; + goto ret; + } + + if (dev->shutdown_thread) { + /* This means the device has been closed (or there + has been an error. An error code of -1 should + be returned. */ + bytes_read = -1; + goto ret; + } + + /* There is no data. Go to sleep and wait for data. */ + + if (milliseconds == -1) { + /* Blocking */ + int res; + res = cond_wait(dev, &dev->condition, &dev->mutex); + if (res == 0) + bytes_read = return_data(dev, data, length); + else { + /* There was an error, or a device disconnection. */ + bytes_read = -1; + } + } + else if (milliseconds > 0) { + /* Non-blocking, but called with timeout. */ + int res; + struct timespec ts; + struct timeval tv; + gettimeofday(&tv, NULL); + TIMEVAL_TO_TIMESPEC(&tv, &ts); + ts.tv_sec += milliseconds / 1000; + ts.tv_nsec += (milliseconds % 1000) * 1000000; + if (ts.tv_nsec >= 1000000000L) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000L; + } + + res = cond_timedwait(dev, &dev->condition, &dev->mutex, &ts); + if (res == 0) + bytes_read = return_data(dev, data, length); + else if (res == ETIMEDOUT) + bytes_read = 0; + else + bytes_read = -1; + } + else { + /* Purely non-blocking */ + bytes_read = 0; + } + +ret: + /* Unlock */ + pthread_mutex_unlock(&dev->mutex); + return bytes_read; +} + +int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) +{ + return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); +} + +int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) +{ + /* All Nonblocking operation is handled by the library. */ + dev->blocking = !nonblock; + + return 0; +} + +int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + return set_report(dev, kIOHIDReportTypeFeature, data, length); +} + +int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + CFIndex len = length - 1; + IOReturn res; + + /* Return if the device has been unplugged. */ + if (dev->disconnected) + return -1; + + res = IOHIDDeviceGetReport(dev->device_handle, + kIOHIDReportTypeFeature, + data[0], /* Report ID */ + data + 1, &len); + if (res == kIOReturnSuccess) + return len + 1; + else + return -1; +} + + +void HID_API_EXPORT hid_close(hid_device *dev) +{ + if (!dev) + return; + + /* Disconnect the report callback before close. */ + if (!dev->disconnected) { + IOHIDDeviceRegisterInputReportCallback( + dev->device_handle, dev->input_report_buf, dev->max_input_report_len, + NULL, dev); + IOHIDDeviceRegisterRemovalCallback(dev->device_handle, NULL, dev); + IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode); + IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode); + } + + /* Cause read_thread() to stop. */ + dev->shutdown_thread = 1; + + /* Wake up the run thread's event loop so that the thread can exit. */ + CFRunLoopSourceSignal(dev->source); + CFRunLoopWakeUp(dev->run_loop); + + /* Notify the read thread that it can shut down now. */ + pthread_barrier_wait(&dev->shutdown_barrier); + + /* Wait for read_thread() to end. */ + pthread_join(dev->thread, NULL); + + /* Close the OS handle to the device, but only if it's not + been unplugged. If it's been unplugged, then calling + IOHIDDeviceClose() will crash. */ + if (!dev->disconnected) { + IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeSeizeDevice); + } + + /* Clear out the queue of received reports. */ + pthread_mutex_lock(&dev->mutex); + while (dev->input_reports) { + return_data(dev, NULL, 0); + } + pthread_mutex_unlock(&dev->mutex); + CFRelease(dev->device_handle); + + free_hid_device(dev); +} + +int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_manufacturer_string(dev->device_handle, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_product_string(dev->device_handle, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_serial_number(dev->device_handle, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + /* TODO: */ + + return 0; +} + + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) +{ + /* TODO: */ + + return NULL; +} + + + + + + + +#if 0 +static int32_t get_location_id(IOHIDDeviceRef device) +{ + return get_int_property(device, CFSTR(kIOHIDLocationIDKey)); +} + +static int32_t get_usage(IOHIDDeviceRef device) +{ + int32_t res; + res = get_int_property(device, CFSTR(kIOHIDDeviceUsageKey)); + if (!res) + res = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey)); + return res; +} + +static int32_t get_usage_page(IOHIDDeviceRef device) +{ + int32_t res; + res = get_int_property(device, CFSTR(kIOHIDDeviceUsagePageKey)); + if (!res) + res = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey)); + return res; +} + +static int get_transport(IOHIDDeviceRef device, wchar_t *buf, size_t len) +{ + return get_string_property(device, CFSTR(kIOHIDTransportKey), buf, len); +} + + +int main(void) +{ + IOHIDManagerRef mgr; + int i; + + mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + IOHIDManagerSetDeviceMatching(mgr, NULL); + IOHIDManagerOpen(mgr, kIOHIDOptionsTypeNone); + + CFSetRef device_set = IOHIDManagerCopyDevices(mgr); + + CFIndex num_devices = CFSetGetCount(device_set); + IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); + CFSetGetValues(device_set, (const void **) device_array); + + for (i = 0; i < num_devices; i++) { + IOHIDDeviceRef dev = device_array[i]; + printf("Device: %p\n", dev); + printf(" %04hx %04hx\n", get_vendor_id(dev), get_product_id(dev)); + + wchar_t serial[256], buf[256]; + char cbuf[256]; + get_serial_number(dev, serial, 256); + + + printf(" Serial: %ls\n", serial); + printf(" Loc: %ld\n", get_location_id(dev)); + get_transport(dev, buf, 256); + printf(" Trans: %ls\n", buf); + make_path(dev, cbuf, 256); + printf(" Path: %s\n", cbuf); + + } + + return 0; +} +#endif diff --git a/src/hidapi/win/hid.c b/src/hidapi/win/hid.c new file mode 100644 index 000000000..4a71e2552 --- /dev/null +++ b/src/hidapi/win/hid.c @@ -0,0 +1,956 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +#include + +#ifndef _NTDEF_ +typedef LONG NTSTATUS; +#endif + +#ifdef __MINGW32__ +#include +#include +#endif + +#ifdef __CYGWIN__ +#include +#define _wcsdup wcsdup +#endif + +/* The maximum number of characters that can be passed into the + HidD_Get*String() functions without it failing.*/ +#define MAX_STRING_WCHARS 0xFFF + +/*#define HIDAPI_USE_DDK*/ + +#ifdef __cplusplus +extern "C" { +#endif + #include + #include + #ifdef HIDAPI_USE_DDK + #include + #endif + + /* Copied from inc/ddk/hidclass.h, part of the Windows DDK. */ + #define HID_OUT_CTL_CODE(id) \ + CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS) + #define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include +#include + + +#include "hidapi.h" + +#undef MIN +#define MIN(x,y) ((x) < (y)? (x): (y)) + +#ifdef _MSC_VER + /* Thanks Microsoft, but I know how to use strncpy(). */ + #pragma warning(disable:4996) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef HIDAPI_USE_DDK + /* Since we're not building with the DDK, and the HID header + files aren't part of the SDK, we have to define all this + stuff here. In lookup_functions(), the function pointers + defined below are set. */ + typedef struct _HIDD_ATTRIBUTES{ + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; + } HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; + + typedef USHORT USAGE; + typedef struct _HIDP_CAPS { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT fields_not_used_by_hidapi[10]; + } HIDP_CAPS, *PHIDP_CAPS; + typedef void* PHIDP_PREPARSED_DATA; + #define HIDP_STATUS_SUCCESS 0x110000 + + typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib); + typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length); + typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length); + typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data); + typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data); + typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps); + typedef BOOLEAN (__stdcall *HidD_SetNumInputBuffers_)(HANDLE handle, ULONG number_buffers); + + static HidD_GetAttributes_ HidD_GetAttributes; + static HidD_GetSerialNumberString_ HidD_GetSerialNumberString; + static HidD_GetManufacturerString_ HidD_GetManufacturerString; + static HidD_GetProductString_ HidD_GetProductString; + static HidD_SetFeature_ HidD_SetFeature; + static HidD_GetFeature_ HidD_GetFeature; + static HidD_GetIndexedString_ HidD_GetIndexedString; + static HidD_GetPreparsedData_ HidD_GetPreparsedData; + static HidD_FreePreparsedData_ HidD_FreePreparsedData; + static HidP_GetCaps_ HidP_GetCaps; + static HidD_SetNumInputBuffers_ HidD_SetNumInputBuffers; + + static HMODULE lib_handle = NULL; + static BOOLEAN initialized = FALSE; +#endif /* HIDAPI_USE_DDK */ + +struct hid_device_ { + HANDLE device_handle; + BOOL blocking; + USHORT output_report_length; + size_t input_report_length; + void *last_error_str; + DWORD last_error_num; + BOOL read_pending; + char *read_buf; + OVERLAPPED ol; +}; + +static hid_device *new_hid_device() +{ + hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); + dev->device_handle = INVALID_HANDLE_VALUE; + dev->blocking = TRUE; + dev->output_report_length = 0; + dev->input_report_length = 0; + dev->last_error_str = NULL; + dev->last_error_num = 0; + dev->read_pending = FALSE; + dev->read_buf = NULL; + memset(&dev->ol, 0, sizeof(dev->ol)); + dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL); + + return dev; +} + +static void free_hid_device(hid_device *dev) +{ + CloseHandle(dev->ol.hEvent); + CloseHandle(dev->device_handle); + LocalFree(dev->last_error_str); + free(dev->read_buf); + free(dev); +} + +static void register_error(hid_device *dev, const char *op) +{ + WCHAR *ptr, *msg; + + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPVOID)&msg, 0/*sz*/, + NULL); + + /* Get rid of the CR and LF that FormatMessage() sticks at the + end of the message. Thanks Microsoft! */ + ptr = msg; + while (*ptr) { + if (*ptr == '\r') { + *ptr = 0x0000; + break; + } + ptr++; + } + + /* Store the message off in the Device entry so that + the hid_error() function can pick it up. */ + LocalFree(dev->last_error_str); + dev->last_error_str = msg; +} + +#ifndef HIDAPI_USE_DDK +static int lookup_functions() +{ + lib_handle = LoadLibraryA("hid.dll"); + if (lib_handle) { +#define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1; + RESOLVE(HidD_GetAttributes); + RESOLVE(HidD_GetSerialNumberString); + RESOLVE(HidD_GetManufacturerString); + RESOLVE(HidD_GetProductString); + RESOLVE(HidD_SetFeature); + RESOLVE(HidD_GetFeature); + RESOLVE(HidD_GetIndexedString); + RESOLVE(HidD_GetPreparsedData); + RESOLVE(HidD_FreePreparsedData); + RESOLVE(HidP_GetCaps); + RESOLVE(HidD_SetNumInputBuffers); +#undef RESOLVE + } + else + return -1; + + return 0; +} +#endif + +static HANDLE open_device(const char *path, BOOL open_rw) +{ + HANDLE handle; + DWORD desired_access = (open_rw)? (GENERIC_WRITE | GENERIC_READ): 0; + DWORD share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE; + + handle = CreateFileA(path, + desired_access, + share_mode, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/ + 0); + + return handle; +} + +int HID_API_EXPORT hid_init(void) +{ +#ifndef HIDAPI_USE_DDK + if (!initialized) { + if (lookup_functions() < 0) { + hid_exit(); + return -1; + } + initialized = TRUE; + } +#endif + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ +#ifndef HIDAPI_USE_DDK + if (lib_handle) + FreeLibrary(lib_handle); + lib_handle = NULL; + initialized = FALSE; +#endif + return 0; +} + +struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + BOOL res; + struct hid_device_info *root = NULL; /* return object */ + struct hid_device_info *cur_dev = NULL; + + /* Windows objects for interacting with the driver. */ + GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} }; + SP_DEVINFO_DATA devinfo_data; + SP_DEVICE_INTERFACE_DATA device_interface_data; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL; + HDEVINFO device_info_set = INVALID_HANDLE_VALUE; + int device_index = 0; + int i; + + if (hid_init() < 0) + return NULL; + + /* Initialize the Windows objects. */ + memset(&devinfo_data, 0x0, sizeof(devinfo_data)); + devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA); + device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + /* Get information for all the devices belonging to the HID class. */ + device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + /* Iterate over each device in the HID class, looking for the right one. */ + + for (;;) { + HANDLE write_handle = INVALID_HANDLE_VALUE; + DWORD required_size = 0; + HIDD_ATTRIBUTES attrib; + + res = SetupDiEnumDeviceInterfaces(device_info_set, + NULL, + &InterfaceClassGuid, + device_index, + &device_interface_data); + + if (!res) { + /* A return of FALSE from this function means that + there are no more devices. */ + break; + } + + /* Call with 0-sized detail size, and let the function + tell us how long the detail struct needs to be. The + size is put in &required_size. */ + res = SetupDiGetDeviceInterfaceDetailA(device_info_set, + &device_interface_data, + NULL, + 0, + &required_size, + NULL); + + /* Allocate a long enough structure for device_interface_detail_data. */ + device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size); + device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); + + /* Get the detailed data for this device. The detail data gives us + the device path for this device, which is then passed into + CreateFile() to get a handle to the device. */ + res = SetupDiGetDeviceInterfaceDetailA(device_info_set, + &device_interface_data, + device_interface_detail_data, + required_size, + NULL, + NULL); + + if (!res) { + /* register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail"); + Continue to the next device. */ + goto cont; + } + + /* Make sure this device is of Setup Class "HIDClass" and has a + driver bound to it. */ + for (i = 0; ; i++) { + char driver_name[256]; + + /* Populate devinfo_data. This function will return failure + when there are no more interfaces left. */ + res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data); + if (!res) + goto cont; + + res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, + SPDRP_CLASS, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); + if (!res) + goto cont; + + if ((strcmp(driver_name, "HIDClass") == 0) || + (strcmp(driver_name, "Mouse") == 0) || + (strcmp(driver_name, "Keyboard") == 0)) { + /* See if there's a driver bound. */ + res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, + SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); + if (res) + break; + } + } + + //wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath); + + /* Open a handle to the device */ + write_handle = open_device(device_interface_detail_data->DevicePath, FALSE); + + /* Check validity of write_handle. */ + if (write_handle == INVALID_HANDLE_VALUE) { + /* Unable to open the device. */ + //register_error(dev, "CreateFile"); + goto cont_close; + } + + + /* Get the Vendor ID and Product ID for this device. */ + attrib.Size = sizeof(HIDD_ATTRIBUTES); + HidD_GetAttributes(write_handle, &attrib); + //wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID); + + /* Check the VID/PID to see if we should add this + device to the enumeration list. */ + if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) && + (product_id == 0x0 || attrib.ProductID == product_id)) { + + #define WSTR_LEN 512 + const char *str; + struct hid_device_info *tmp; + PHIDP_PREPARSED_DATA pp_data = NULL; + HIDP_CAPS caps; + BOOLEAN res; + NTSTATUS nt_res; + wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */ + size_t len; + + /* VID/PID match. Create the record. */ + tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; + + /* Get the Usage Page and Usage for this device. */ + res = HidD_GetPreparsedData(write_handle, &pp_data); + if (res) { + nt_res = HidP_GetCaps(pp_data, &caps); + if (nt_res == HIDP_STATUS_SUCCESS) { + cur_dev->usage_page = caps.UsagePage; + cur_dev->usage = caps.Usage; + } + + HidD_FreePreparsedData(pp_data); + } + + /* Fill out the record */ + cur_dev->next = NULL; + str = device_interface_detail_data->DevicePath; + if (str) { + len = strlen(str); + cur_dev->path = (char*) calloc(len+1, sizeof(char)); + strncpy(cur_dev->path, str, len+1); + cur_dev->path[len] = '\0'; + } + else + cur_dev->path = NULL; + + /* Serial Number */ + res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->serial_number = _wcsdup(wstr); + } + + /* Manufacturer String */ + res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->manufacturer_string = _wcsdup(wstr); + } + + /* Product String */ + res = HidD_GetProductString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->product_string = _wcsdup(wstr); + } + + /* VID/PID */ + cur_dev->vendor_id = attrib.VendorID; + cur_dev->product_id = attrib.ProductID; + + /* Release Number */ + cur_dev->release_number = attrib.VersionNumber; + + /* Interface Number. It can sometimes be parsed out of the path + on Windows if a device has multiple interfaces. See + http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or + search for "Hardware IDs for HID Devices" at MSDN. If it's not + in the path, it's set to -1. */ + cur_dev->interface_number = -1; + if (cur_dev->path) { + char *interface_component = strstr(cur_dev->path, "&mi_"); + if (interface_component) { + char *hex_str = interface_component + 4; + char *endptr = NULL; + cur_dev->interface_number = strtol(hex_str, &endptr, 16); + if (endptr == hex_str) { + /* The parsing failed. Set interface_number to -1. */ + cur_dev->interface_number = -1; + } + } + } + } + +cont_close: + CloseHandle(write_handle); +cont: + /* We no longer need the detail data. It can be freed */ + free(device_interface_detail_data); + + device_index++; + + } + + /* Close the device information handle. */ + SetupDiDestroyDeviceInfoList(device_info_set); + + return root; + +} + +void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs) +{ + /* TODO: Merge this with the Linux version. This function is platform-independent. */ + struct hid_device_info *d = devs; + while (d) { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + + +HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) +{ + /* TODO: Merge this functions with the Linux version. This function should be platform independent. */ + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + devs = hid_enumerate(vendor_id, product_id); + cur_dev = devs; + while (cur_dev) { + if (cur_dev->vendor_id == vendor_id && + cur_dev->product_id == product_id) { + if (serial_number) { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) { + path_to_open = cur_dev->path; + break; + } + } + else { + path_to_open = cur_dev->path; + break; + } + } + cur_dev = cur_dev->next; + } + + if (path_to_open) { + /* Open the device */ + handle = hid_open_path(path_to_open); + } + + hid_free_enumeration(devs); + + return handle; +} + +HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) +{ + hid_device *dev; + HIDP_CAPS caps; + PHIDP_PREPARSED_DATA pp_data = NULL; + BOOLEAN res; + NTSTATUS nt_res; + + if (hid_init() < 0) { + return NULL; + } + + dev = new_hid_device(); + + /* Open a handle to the device */ + dev->device_handle = open_device(path, TRUE); + + /* Check validity of write_handle. */ + if (dev->device_handle == INVALID_HANDLE_VALUE) { + /* System devices, such as keyboards and mice, cannot be opened in + read-write mode, because the system takes exclusive control over + them. This is to prevent keyloggers. However, feature reports + can still be sent and received. Retry opening the device, but + without read/write access. */ + dev->device_handle = open_device(path, FALSE); + + /* Check the validity of the limited device_handle. */ + if (dev->device_handle == INVALID_HANDLE_VALUE) { + /* Unable to open the device, even without read-write mode. */ + register_error(dev, "CreateFile"); + goto err; + } + } + + /* Set the Input Report buffer size to 64 reports. */ + res = HidD_SetNumInputBuffers(dev->device_handle, 64); + if (!res) { + register_error(dev, "HidD_SetNumInputBuffers"); + goto err; + } + + /* Get the Input Report length for the device. */ + res = HidD_GetPreparsedData(dev->device_handle, &pp_data); + if (!res) { + register_error(dev, "HidD_GetPreparsedData"); + goto err; + } + nt_res = HidP_GetCaps(pp_data, &caps); + if (nt_res != HIDP_STATUS_SUCCESS) { + register_error(dev, "HidP_GetCaps"); + goto err_pp_data; + } + dev->output_report_length = caps.OutputReportByteLength; + dev->input_report_length = caps.InputReportByteLength; + HidD_FreePreparsedData(pp_data); + + dev->read_buf = (char*) malloc(dev->input_report_length); + + return dev; + +err_pp_data: + HidD_FreePreparsedData(pp_data); +err: + free_hid_device(dev); + return NULL; +} + +int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + DWORD bytes_written; + BOOL res; + + OVERLAPPED ol; + unsigned char *buf; + memset(&ol, 0, sizeof(ol)); + + /* Make sure the right number of bytes are passed to WriteFile. Windows + expects the number of bytes which are in the _longest_ report (plus + one for the report number) bytes even if the data is a report + which is shorter than that. Windows gives us this value in + caps.OutputReportByteLength. If a user passes in fewer bytes than this, + create a temporary buffer which is the proper size. */ + if (length >= dev->output_report_length) { + /* The user passed the right number of bytes. Use the buffer as-is. */ + buf = (unsigned char *) data; + } else { + /* Create a temporary buffer and copy the user's data + into it, padding the rest with zeros. */ + buf = (unsigned char *) malloc(dev->output_report_length); + memcpy(buf, data, length); + memset(buf + length, 0, dev->output_report_length - length); + length = dev->output_report_length; + } + + res = WriteFile(dev->device_handle, buf, length, NULL, &ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + /* WriteFile() failed. Return error. */ + register_error(dev, "WriteFile"); + bytes_written = -1; + goto end_of_function; + } + } + + /* Wait here until the write is done. This makes + hid_write() synchronous. */ + res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/); + if (!res) { + /* The Write operation failed. */ + register_error(dev, "WriteFile"); + bytes_written = -1; + goto end_of_function; + } + +end_of_function: + if (buf != data) + free(buf); + + return bytes_written; +} + + +int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + DWORD bytes_read = 0; + size_t copy_len = 0; + BOOL res; + + /* Copy the handle for convenience. */ + HANDLE ev = dev->ol.hEvent; + + if (!dev->read_pending) { + /* Start an Overlapped I/O read. */ + dev->read_pending = TRUE; + memset(dev->read_buf, 0, dev->input_report_length); + ResetEvent(ev); + res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + /* ReadFile() has failed. + Clean up and return error. */ + CancelIo(dev->device_handle); + dev->read_pending = FALSE; + goto end_of_function; + } + } + } + + if (milliseconds >= 0) { + /* See if there is any data yet. */ + res = WaitForSingleObject(ev, milliseconds); + if (res != WAIT_OBJECT_0) { + /* There was no data this time. Return zero bytes available, + but leave the Overlapped I/O running. */ + return 0; + } + } + + /* Either WaitForSingleObject() told us that ReadFile has completed, or + we are in non-blocking mode. Get the number of bytes read. The actual + data has been copied to the data[] array which was passed to ReadFile(). */ + res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/); + + /* Set pending back to false, even if GetOverlappedResult() returned error. */ + dev->read_pending = FALSE; + + if (res && bytes_read > 0) { + if (dev->read_buf[0] == 0x0) { + /* If report numbers aren't being used, but Windows sticks a report + number (0x0) on the beginning of the report anyway. To make this + work like the other platforms, and to make it work more like the + HID spec, we'll skip over this byte. */ + bytes_read--; + copy_len = length > bytes_read ? bytes_read : length; + memcpy(data, dev->read_buf+1, copy_len); + } + else { + /* Copy the whole buffer, report number and all. */ + copy_len = length > bytes_read ? bytes_read : length; + memcpy(data, dev->read_buf, copy_len); + } + } + +end_of_function: + if (!res) { + register_error(dev, "GetOverlappedResult"); + return -1; + } + + return copy_len; +} + +int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length) +{ + return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); +} + +int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock) +{ + dev->blocking = !nonblock; + return 0; /* Success */ +} + +int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, length); + if (!res) { + register_error(dev, "HidD_SetFeature"); + return -1; + } + + return length; +} + + +int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + BOOL res; +#if 0 + res = HidD_GetFeature(dev->device_handle, data, length); + if (!res) { + register_error(dev, "HidD_GetFeature"); + return -1; + } + return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */ +#else + DWORD bytes_returned; + + OVERLAPPED ol; + memset(&ol, 0, sizeof(ol)); + + res = DeviceIoControl(dev->device_handle, + IOCTL_HID_GET_FEATURE, + data, length, + data, length, + &bytes_returned, &ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + /* DeviceIoControl() failed. Return error. */ + register_error(dev, "Send Feature Report DeviceIoControl"); + return -1; + } + } + + /* Wait here until the write is done. This makes + hid_get_feature_report() synchronous. */ + res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/); + if (!res) { + /* The operation failed. */ + register_error(dev, "Send Feature Report GetOverLappedResult"); + return -1; + } + + /* bytes_returned does not include the first byte which contains the + report ID. The data buffer actually contains one more byte than + bytes_returned. */ + bytes_returned++; + + return bytes_returned; +#endif +} + +void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev) +{ + if (!dev) + return; + CancelIo(dev->device_handle); + free_hid_device(dev); +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); + if (!res) { + register_error(dev, "HidD_GetManufacturerString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); + if (!res) { + register_error(dev, "HidD_GetProductString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); + if (!res) { + register_error(dev, "HidD_GetSerialNumberString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); + if (!res) { + register_error(dev, "HidD_GetIndexedString"); + return -1; + } + + return 0; +} + + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) +{ + return (wchar_t*)dev->last_error_str; +} + + +/*#define PICPGM*/ +/*#define S11*/ +#define P32 +#ifdef S11 + unsigned short VendorID = 0xa0a0; + unsigned short ProductID = 0x0001; +#endif + +#ifdef P32 + unsigned short VendorID = 0x04d8; + unsigned short ProductID = 0x3f; +#endif + + +#ifdef PICPGM + unsigned short VendorID = 0x04d8; + unsigned short ProductID = 0x0033; +#endif + + +#if 0 +int __cdecl main(int argc, char* argv[]) +{ + int res; + unsigned char buf[65]; + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + + /* Set up the command buffer. */ + memset(buf,0x00,sizeof(buf)); + buf[0] = 0; + buf[1] = 0x81; + + + /* Open the device. */ + int handle = open(VendorID, ProductID, L"12345"); + if (handle < 0) + printf("unable to open device\n"); + + + /* Toggle LED (cmd 0x80) */ + buf[1] = 0x80; + res = write(handle, buf, 65); + if (res < 0) + printf("Unable to write()\n"); + + /* Request state (cmd 0x81) */ + buf[1] = 0x81; + write(handle, buf, 65); + if (res < 0) + printf("Unable to write() (2)\n"); + + /* Read requested state */ + read(handle, buf, 65); + if (res < 0) + printf("Unable to read()\n"); + + /* Print out the returned buffer. */ + for (int i = 0; i < 4; i++) + printf("buf[%d]: %d\n", i, buf[i]); + + return 0; +} +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 51d092094..da982c49a 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -32,4 +32,13 @@ #define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1) +//==================== +// 2.0.0.alpha1 techs +//==================== +#define ENABLE_2_0_0_ALPHA1 1 + +// Enabled 3Dconnexion devices +#define ENABLE_3DCONNEXION_DEVICES (1 && ENABLE_2_0_0_ALPHA1) + + #endif // _technologies_h_ diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 17b76e629..6218f3837 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -136,6 +136,8 @@ set(SLIC3R_GUI_SOURCES GUI/ProgressStatusBar.cpp GUI/PrintHostDialogs.cpp GUI/PrintHostDialogs.hpp + GUI/Mouse3DController.cpp + GUI/Mouse3DController.hpp Utils/Http.cpp Utils/Http.hpp Utils/FixModelByWin10.cpp @@ -167,7 +169,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) encoding_check(libslic3r_gui) -target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES}) +target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES} ${HIDAPI_LIBRARIES}) if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE) endif () diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 715d9f806..2960d08c3 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -22,6 +22,9 @@ #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" #include "GUI_ObjectManipulation.hpp" +#if ENABLE_3DCONNEXION_DEVICES +#include "Mouse3DController.hpp" +#endif // ENABLE_3DCONNEXION_DEVICES #include "I18N.hpp" #if ENABLE_RETINA_GL @@ -2305,14 +2308,28 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) m_dirty |= m_main_toolbar.update_items_state(); m_dirty |= m_undoredo_toolbar.update_items_state(); m_dirty |= m_view_toolbar.update_items_state(); +#if ENABLE_3DCONNEXION_DEVICES + bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(); + m_dirty |= mouse3d_controller_applied; +#endif // ENABLE_3DCONNEXION_DEVICES if (!m_dirty) return; _refresh_if_shown_on_screen(); +#if ENABLE_3DCONNEXION_DEVICES + if (m_keep_dirty || wxGetApp().plater()->get_mouse3d_controller().is_device_connected()) + { + m_dirty = true; + evt.RequestMore(); + } + else + m_dirty = false; +#else if (m_keep_dirty) m_dirty = true; +#endif // ENABLE_3DCONNEXION_DEVICES } void GLCanvas3D::on_char(wxKeyEvent& evt) @@ -2531,6 +2548,13 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) { +#if ENABLE_3DCONNEXION_DEVICES + // try to filter out events coming from mouse 3d controller + const Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller(); + if (controller.has_rotation() || controller.has_translation()) + return; +#endif // ENABLE_3DCONNEXION_DEVICES + if (!m_initialized) return; @@ -3815,7 +3839,9 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) // updates camera m_camera.apply_viewport(0, 0, w, h); +#if !ENABLE_3DCONNEXION_DEVICES m_dirty = false; +#endif // !ENABLE_3DCONNEXION_DEVICES } BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_bed_model) const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index fb767360c..cce6b0f2b 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -486,6 +486,9 @@ public: void set_color_by(const std::string& value); const Camera& get_camera() const { return m_camera; } +#if ENABLE_3DCONNEXION_DEVICES + Camera& get_camera() { return m_camera; } +#endif // ENABLE_3DCONNEXION_DEVICES BoundingBoxf3 volumes_bounding_box() const; BoundingBoxf3 scene_bounding_box() const; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index dfe3a9cf9..ae0413937 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -24,6 +24,9 @@ #include "PrintHostDialogs.hpp" #include "wxExtensions.hpp" #include "GUI_ObjectList.hpp" +#if ENABLE_3DCONNEXION_DEVICES +#include "Mouse3DController.hpp" +#endif // ENABLE_3DCONNEXION_DEVICES #include "I18N.hpp" #include @@ -108,6 +111,10 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S } if(m_plater) m_plater->stop_jobs(); +#if ENABLE_3DCONNEXION_DEVICES + if (m_plater != nullptr) + m_plater->get_mouse3d_controller().set_canvas(nullptr); +#endif // ENABLE_3DCONNEXION_DEVICES // Weird things happen as the Paint messages are floating around the windows being destructed. // Avoid the Paint messages by hiding the main window. diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp new file mode 100644 index 000000000..43faf852b --- /dev/null +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -0,0 +1,332 @@ +#include "libslic3r/libslic3r.h" +#include "Mouse3DController.hpp" + +#if ENABLE_3DCONNEXION_DEVICES + +#include "GLCanvas3D.hpp" +#include "GUI_App.hpp" +#include "PresetBundle.hpp" + +#include + +static const std::vector _3DCONNEXION_VENDORS = +{ + 0x046d, // LOGITECH = 1133 // Logitech (3Dconnexion is made by Logitech) + 0x256F // 3DCONNECTION = 9583 // 3Dconnexion +}; + +static const std::vector _3DCONNEXION_DEVICES = +{ + 0xC623, // TRAVELER = 50723 + 0xC626, // NAVIGATOR = 50726 + 0xc628, // NAVIGATOR_FOR_NOTEBOOKS = 50728 + 0xc627, // SPACEEXPLORER = 50727 + 0xC603, // SPACEMOUSE = 50691 + 0xC62B, // SPACEMOUSEPRO = 50731 + 0xc621, // SPACEBALL5000 = 50721 + 0xc625, // SPACEPILOT = 50725 + 0xc629 // SPACEPILOTPRO = 50729 +}; + +static const unsigned int _3DCONNEXION_BUTTONS_COUNT = 2; + +namespace Slic3r { +namespace GUI { + +const double Mouse3DController::State::DefaultTranslationScale = 2.5; +const float Mouse3DController::State::DefaultRotationScale = 1.0; + +Mouse3DController::State::State() + : m_translation(Vec3d::Zero()) + , m_rotation(Vec3f::Zero()) + , m_buttons(_3DCONNEXION_BUTTONS_COUNT, false) + , m_translation_scale(DefaultTranslationScale) + , m_rotation_scale(DefaultRotationScale) +{ +} + +void Mouse3DController::State::set_translation(const Vec3d& translation) +{ + std::lock_guard lock(m_mutex); + m_translation = translation; +} + +void Mouse3DController::State::set_rotation(const Vec3f& rotation) +{ + std::lock_guard lock(m_mutex); + m_rotation = rotation; +} + +void Mouse3DController::State::set_button(unsigned int id) +{ + std::lock_guard lock(m_mutex); + if (id < _3DCONNEXION_BUTTONS_COUNT) + m_buttons[id] = true; +} + +bool Mouse3DController::State::has_translation() const +{ + std::lock_guard lock(m_mutex); + return !m_translation.isApprox(Vec3d::Zero()); +} +bool Mouse3DController::State::has_rotation() const +{ + std::lock_guard lock(m_mutex); + return !m_rotation.isApprox(Vec3f::Zero()); +} + +bool Mouse3DController::State::has_any_button() const +{ + std::lock_guard lock(m_mutex); + for (int i = 0; i < _3DCONNEXION_BUTTONS_COUNT; ++i) + { + if (m_buttons[i]) + return true; + } + return false; +} + +bool Mouse3DController::State::apply(GLCanvas3D& canvas) +{ + if (!wxGetApp().IsActive()) + return false; + + bool ret = false; + Camera& camera = canvas.get_camera(); + + if (has_translation()) + { + camera.set_target(camera.get_target() + m_translation_scale * (m_translation(0) * camera.get_dir_right() + m_translation(1) * camera.get_dir_forward() + m_translation(2) * camera.get_dir_up())); + m_translation = Vec3d::Zero(); + ret = true; + } + + if (has_rotation()) + { + float theta = m_rotation_scale * m_rotation(0); + float phi = m_rotation_scale * m_rotation(2); + float sign = camera.inverted_phi ? -1.0f : 1.0f; + camera.phi += sign * phi; + camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); + m_rotation = Vec3f::Zero(); + ret = true; + } + + if (has_any_button()) + { + if (m_buttons[0]) + canvas.set_camera_zoom(1.0); + else if (m_buttons[1]) + canvas.set_camera_zoom(-1.0); + + m_buttons = std::vector(_3DCONNEXION_BUTTONS_COUNT, false); + ret = true; + } + + return ret; +} + +Mouse3DController::Mouse3DController() + : m_initialized(false) + , m_canvas(nullptr) + , m_device(nullptr) + , m_running(false) +{ +} + +void Mouse3DController::init() +{ + if (m_initialized) + return; + + // Initialize the hidapi library + int res = hid_init(); + if (res != 0) + return; + + m_initialized = true; + + connect_device(); + start(); +} + +void Mouse3DController::shutdown() +{ + if (!m_initialized) + return; + + stop(); + + if (m_thread.joinable()) + m_thread.join(); + + disconnect_device(); + + // Finalize the hidapi library + hid_exit(); + m_initialized = false; +} + +void Mouse3DController::connect_device() +{ + if (m_device != nullptr) + return; + + // Enumerates devices + hid_device_info* devices = hid_enumerate(0, 0); + if (devices == nullptr) + return; + + // Searches for 1st connected 3Dconnexion device + unsigned short vendor_id = 0; + unsigned short product_id = 0; + + hid_device_info* current = devices; + while (current != nullptr) + { + for (size_t i = 0; i < _3DCONNEXION_VENDORS.size(); ++i) + { + if (_3DCONNEXION_VENDORS[i] == current->vendor_id) + { + vendor_id = current->vendor_id; + break; + } + } + + if (vendor_id != 0) + { + for (size_t i = 0; i < _3DCONNEXION_DEVICES.size(); ++i) + { + if (_3DCONNEXION_DEVICES[i] == current->product_id) + { + product_id = current->product_id; + break; + } + } + + if (product_id == 0) + vendor_id = 0; + } + + if (vendor_id != 0) + break; + + current = current->next; + } + + // Free enumerated devices + hid_free_enumeration(devices); + + if (vendor_id == 0) + return; + + // Open the 3Dconnexion device using the VID, PID + m_device = hid_open(vendor_id, product_id, nullptr); +} + +void Mouse3DController::disconnect_device() +{ + if (m_device == nullptr) + return; + + // Close the 3Dconnexion device + hid_close(m_device); + m_device = nullptr; +} + +void Mouse3DController::start() +{ + if ((m_device == nullptr) || m_running) + return; + + m_thread = std::thread(&Mouse3DController::run, this); +} + +void Mouse3DController::run() +{ + m_running = true; + while (m_running) + { + collect_input(); + } +} + +double convert_input(int first, unsigned char val) +{ + int ret = 0; + + switch (val) + { + case 0: { ret = first; break; } + case 1: { ret = first + 255; break; } + case 254: { ret = -511 + first; break; } + case 255: { ret = -255 + first; break; } + default: { break; } + } + + return (double)ret / 349.0; +} + +void Mouse3DController::collect_input() +{ + // Read data from device + enum EDataType + { + Translation = 1, + Rotation, + Button + }; + + unsigned char retrieved_data[8] = { 0 }; + int res = hid_read_timeout(m_device, retrieved_data, sizeof(retrieved_data), 100); + if (res < 0) + { + // An error occourred (device detached from pc ?) + stop(); + return; + } + + if (res > 0) + { + switch (retrieved_data[0]) + { + case Translation: + { + Vec3d translation(-convert_input((int)retrieved_data[1], retrieved_data[2]), + convert_input((int)retrieved_data[3], retrieved_data[4]), + convert_input((int)retrieved_data[5], retrieved_data[6])); + if (!translation.isApprox(Vec3d::Zero())) + m_state.set_translation(translation); + + break; + } + case Rotation: + { + Vec3f rotation(-(float)convert_input((int)retrieved_data[1], retrieved_data[2]), + (float)convert_input((int)retrieved_data[3], retrieved_data[4]), + -(float)convert_input((int)retrieved_data[5], retrieved_data[6])); + if (!rotation.isApprox(Vec3f::Zero())) + m_state.set_rotation(rotation); + + break; + } + case Button: + { + for (unsigned int i = 0; i < _3DCONNEXION_BUTTONS_COUNT; ++i) + { + if (retrieved_data[1] & (0x1 << i)) + m_state.set_button(i); + } + + break; + } + default: + break; + } + } +} + +} // namespace GUI +} // namespace Slic3r + +#endif // ENABLE_3DCONNEXION_DEVICES diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp new file mode 100644 index 000000000..b62a00b6f --- /dev/null +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -0,0 +1,96 @@ +#ifndef slic3r_Mouse3DController_hpp_ +#define slic3r_Mouse3DController_hpp_ + +#if ENABLE_3DCONNEXION_DEVICES + +#include "hidapi/hidapi.h" +#include +#include +#include + +namespace Slic3r { +namespace GUI { + +class GLCanvas3D; + +class Mouse3DController +{ + class State + { + static const double DefaultTranslationScale; + static const float DefaultRotationScale; + + mutable std::mutex m_mutex; + + Vec3d m_translation; + Vec3f m_rotation; + std::vector m_buttons; + + double m_translation_scale; + float m_rotation_scale; + + public: + State(); + + void set_translation(const Vec3d& translation); + void set_rotation(const Vec3f& rotation); + void set_button(unsigned int id); + + bool has_translation() const; + bool has_rotation() const; + bool has_any_button() const; + + // return true if any change to the camera took place + bool apply(GLCanvas3D& canvas); + }; + + bool m_initialized; + State m_state; + std::thread m_thread; + GLCanvas3D* m_canvas; + std::mutex m_mutex; + hid_device* m_device; + bool m_running; + +public: + Mouse3DController(); + + void init(); + void shutdown(); + + bool is_device_connected() const { return m_device != nullptr; } + bool is_running() const { return m_running; } + + void set_canvas(GLCanvas3D* canvas) + { + std::lock_guard lock(m_mutex); + m_canvas = canvas; + } + + bool has_translation() const { return m_state.has_translation(); } + bool has_rotation() const { return m_state.has_rotation(); } + bool has_any_button() const { return m_state.has_any_button(); } + + bool apply() + { + std::lock_guard lock(m_mutex); + return (m_canvas != nullptr) ? m_state.apply(*m_canvas) : false; + } + +private: + void connect_device(); + void disconnect_device(); + void start(); + void stop() { m_running = false; } + + void run(); + void collect_input(); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // ENABLE_3DCONNEXION_DEVICES + +#endif // slic3r_Mouse3DController_hpp_ + diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c7f1f4c5c..b721ee8fd 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -61,6 +61,9 @@ #include "GUI_Preview.hpp" #include "3DBed.hpp" #include "Camera.hpp" +#if ENABLE_3DCONNEXION_DEVICES +#include "Mouse3DController.hpp" +#endif // ENABLE_3DCONNEXION_DEVICES #include "Tab.hpp" #include "PresetBundle.hpp" #include "BackgroundSlicingProcess.hpp" @@ -1367,6 +1370,9 @@ struct Plater::priv Sidebar *sidebar; Bed3D bed; Camera camera; +#if ENABLE_3DCONNEXION_DEVICES + Mouse3DController mouse3d_controller; +#endif // ENABLE_3DCONNEXION_DEVICES View3D* view3D; GLToolbar view_toolbar; Preview *preview; @@ -2094,12 +2100,20 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // updates camera type from .ini file camera.set_type(get_config("use_perspective_camera")); +#if ENABLE_3DCONNEXION_DEVICES + mouse3d_controller.init(); +#endif // ENABLE_3DCONNEXION_DEVICES + // Initialize the Undo / Redo stack with a first snapshot. this->take_snapshot(_(L("New Project"))); } Plater::priv::~priv() { +#if ENABLE_3DCONNEXION_DEVICES + mouse3d_controller.shutdown(); +#endif // ENABLE_3DCONNEXION_DEVICES + if (config != nullptr) delete config; } @@ -3248,6 +3262,11 @@ void Plater::priv::set_current_panel(wxPanel* panel) } else view3D->reload_scene(true); } + +#if ENABLE_3DCONNEXION_DEVICES + mouse3d_controller.set_canvas(view3D->get_canvas3d()); +#endif // ENABLE_3DCONNEXION_DEVICES + // sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) view3D->set_as_dirty(); view_toolbar.select_item("3D"); @@ -3262,6 +3281,11 @@ void Plater::priv::set_current_panel(wxPanel* panel) this->q->reslice(); // keeps current gcode preview, if any preview->reload_print(true); + +#if ENABLE_3DCONNEXION_DEVICES + mouse3d_controller.set_canvas(preview->get_canvas3d()); +#endif // ENABLE_3DCONNEXION_DEVICES + preview->set_canvas_as_dirty(); view_toolbar.select_item("Preview"); } @@ -5020,6 +5044,18 @@ const Camera& Plater::get_camera() const return p->camera; } +#if ENABLE_3DCONNEXION_DEVICES +const Mouse3DController& Plater::get_mouse3d_controller() const +{ + return p->mouse3d_controller; +} + +Mouse3DController& Plater::get_mouse3d_controller() +{ + return p->mouse3d_controller; +} +#endif // ENABLE_3DCONNEXION_DEVICES + bool Plater::can_delete() const { return p->can_delete(); } bool Plater::can_delete_all() const { return p->can_delete_all(); } bool Plater::can_increase_instances() const { return p->can_increase_instances(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 26dcb5ac3..c217c8217 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -41,6 +41,7 @@ class ObjectSettings; class ObjectLayers; class ObjectList; class GLCanvas3D; +class Mouse3DController; using t_optgroups = std::vector >; @@ -251,6 +252,10 @@ public: void msw_rescale(); const Camera& get_camera() const; +#if ENABLE_3DCONNEXION_DEVICES + const Mouse3DController& get_mouse3d_controller() const; + Mouse3DController& get_mouse3d_controller(); +#endif // ENABLE_3DCONNEXION_DEVICES // ROII wrapper for suppressing the Undo / Redo snapshot to be taken. class SuppressSnapshots From 33bfc925c2a65861fbc77f97adeb6bfe59e793d2 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 27 Sep 2019 15:02:38 +0200 Subject: [PATCH 02/73] ENABLE_3DCONNEXION_DEVICES -> try a different hid.c for hidapi library on Linux --- src/hidapi/linux/hid.c | 1723 ++++++++++++++++++++++++++++------------ 1 file changed, 1220 insertions(+), 503 deletions(-) diff --git a/src/hidapi/linux/hid.c b/src/hidapi/linux/hid.c index 56dac0fab..8cd5e3dca 100644 --- a/src/hidapi/linux/hid.c +++ b/src/hidapi/linux/hid.c @@ -6,7 +6,9 @@ Signal 11 Software 8/22/2009 - Linux Version - 6/2/2009 + Linux Version - 6/2/2010 + Libusb Version - 8/13/2010 + FreeBSD Version - 11/1/2011 Copyright 2009, All Rights Reserved. @@ -21,10 +23,13 @@ http://github.com/signal11/hidapi . ********************************************************/ +#define _GNU_SOURCE /* needed for wcsdup() before glibc 2.10 */ + /* C */ #include #include #include +#include #include #include @@ -35,124 +40,219 @@ #include #include #include -#include +#include +#include -/* Linux */ -#include -#include -#include -#include +/* GNU / LibUSB */ +#include +#ifndef __ANDROID__ +#include +#endif #include "hidapi.h" -/* Definitions from linux/hidraw.h. Since these are new, some distros - may not have header files which contain them. */ -#ifndef HIDIOCSFEATURE -#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len) -#endif -#ifndef HIDIOCGFEATURE -#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len) -#endif +#ifdef __ANDROID__ +/* Barrier implementation because Android/Bionic don't have pthread_barrier. + This implementation came from Brent Priddy and was posted on + StackOverflow. It is used with his permission. */ +typedef int pthread_barrierattr_t; +typedef struct pthread_barrier { + pthread_mutex_t mutex; + pthread_cond_t cond; + int count; + int trip_count; +} pthread_barrier_t; -/* USB HID device property names */ -const char *device_string_names[] = { - "manufacturer", - "product", - "serial", -}; - -/* Symbolic names for the properties above */ -enum device_string_id { - DEVICE_STRING_MANUFACTURER, - DEVICE_STRING_PRODUCT, - DEVICE_STRING_SERIAL, - - DEVICE_STRING_COUNT, -}; - -struct hid_device_ { - int device_handle; - int blocking; - int uses_numbered_reports; -}; - - -static __u32 kernel_version = 0; - -static __u32 detect_kernel_version(void) +static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) { - struct utsname name; - int major, minor, release; - int ret; - - uname(&name); - ret = sscanf(name.release, "%d.%d.%d", &major, &minor, &release); - if (ret == 3) { - return KERNEL_VERSION(major, minor, release); + if(count == 0) { + errno = EINVAL; + return -1; } - ret = sscanf(name.release, "%d.%d", &major, &minor); - if (ret == 2) { - return KERNEL_VERSION(major, minor, 0); + if(pthread_mutex_init(&barrier->mutex, 0) < 0) { + return -1; } + if(pthread_cond_init(&barrier->cond, 0) < 0) { + pthread_mutex_destroy(&barrier->mutex); + return -1; + } + barrier->trip_count = count; + barrier->count = 0; - printf("Couldn't determine kernel version from version string \"%s\"\n", name.release); return 0; } +static int pthread_barrier_destroy(pthread_barrier_t *barrier) +{ + pthread_cond_destroy(&barrier->cond); + pthread_mutex_destroy(&barrier->mutex); + return 0; +} + +static int pthread_barrier_wait(pthread_barrier_t *barrier) +{ + pthread_mutex_lock(&barrier->mutex); + ++(barrier->count); + if(barrier->count >= barrier->trip_count) + { + barrier->count = 0; + pthread_cond_broadcast(&barrier->cond); + pthread_mutex_unlock(&barrier->mutex); + return 1; + } + else + { + pthread_cond_wait(&barrier->cond, &(barrier->mutex)); + pthread_mutex_unlock(&barrier->mutex); + return 0; + } +} + +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef DEBUG_PRINTF +#define LOG(...) fprintf(stderr, __VA_ARGS__) +#else +#define LOG(...) do {} while (0) +#endif + +#ifndef __FreeBSD__ +#define DETACH_KERNEL_DRIVER +#endif + +/* Uncomment to enable the retrieval of Usage and Usage Page in +hid_enumerate(). Warning, on platforms different from FreeBSD +this is very invasive as it requires the detach +and re-attach of the kernel driver. See comments inside hid_enumerate(). +libusb HIDAPI programs are encouraged to use the interface number +instead to differentiate between interfaces on a composite HID device. */ +/*#define INVASIVE_GET_USAGE*/ + +/* Linked List of input reports received from the device. */ +struct input_report { + uint8_t *data; + size_t len; + struct input_report *next; +}; + + +struct hid_device_ { + /* Handle to the actual device. */ + libusb_device_handle *device_handle; + + /* Endpoint information */ + int input_endpoint; + int output_endpoint; + int input_ep_max_packet_size; + + /* The interface number of the HID */ + int interface; + + /* Indexes of Strings */ + int manufacturer_index; + int product_index; + int serial_index; + + /* Whether blocking reads are used */ + int blocking; /* boolean */ + + /* Read thread objects */ + pthread_t thread; + pthread_mutex_t mutex; /* Protects input_reports */ + pthread_cond_t condition; + pthread_barrier_t barrier; /* Ensures correct startup sequence */ + int shutdown_thread; + int cancelled; + struct libusb_transfer *transfer; + + /* List of received input reports. */ + struct input_report *input_reports; +}; + +static libusb_context *usb_context = NULL; + +uint16_t get_usb_code_for_current_locale(void); +static int return_data(hid_device *dev, unsigned char *data, size_t length); + static hid_device *new_hid_device(void) { hid_device *dev = calloc(1, sizeof(hid_device)); - dev->device_handle = -1; dev->blocking = 1; - dev->uses_numbered_reports = 0; + + pthread_mutex_init(&dev->mutex, NULL); + pthread_cond_init(&dev->condition, NULL); + pthread_barrier_init(&dev->barrier, NULL, 2); return dev; } - -/* The caller must free the returned string with free(). */ -static wchar_t *utf8_to_wchar_t(const char *utf8) +static void free_hid_device(hid_device *dev) { - wchar_t *ret = NULL; + /* Clean up the thread objects */ + pthread_barrier_destroy(&dev->barrier); + pthread_cond_destroy(&dev->condition); + pthread_mutex_destroy(&dev->mutex); - if (utf8) { - size_t wlen = mbstowcs(NULL, utf8, 0); - if ((size_t) -1 == wlen) { - return wcsdup(L""); - } - ret = calloc(wlen+1, sizeof(wchar_t)); - mbstowcs(ret, utf8, wlen+1); - ret[wlen] = 0x0000; + /* Free the device itself */ + free(dev); +} + +#if 0 +/*TODO: Implement this funciton on hidapi/libusb.. */ +static void register_error(hid_device *dev, const char *op) +{ + +} +#endif + +#ifdef INVASIVE_GET_USAGE +/* Get bytes from a HID Report Descriptor. + Only call with a num_bytes of 0, 1, 2, or 4. */ +static uint32_t get_bytes(uint8_t *rpt, size_t len, size_t num_bytes, size_t cur) +{ + /* Return if there aren't enough bytes. */ + if (cur + num_bytes >= len) + return 0; + + if (num_bytes == 0) + return 0; + else if (num_bytes == 1) { + return rpt[cur+1]; } - - return ret; + else if (num_bytes == 2) { + return (rpt[cur+2] * 256 + rpt[cur+1]); + } + else if (num_bytes == 4) { + return (rpt[cur+4] * 0x01000000 + + rpt[cur+3] * 0x00010000 + + rpt[cur+2] * 0x00000100 + + rpt[cur+1] * 0x00000001); + } + else + return 0; } -/* Get an attribute value from a udev_device and return it as a whar_t - string. The returned string must be freed with free() when done.*/ -static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name) +/* Retrieves the device's Usage Page and Usage from the report + descriptor. The algorithm is simple, as it just returns the first + Usage and Usage Page that it finds in the descriptor. + The return value is 0 on success and -1 on failure. */ +static int get_usage(uint8_t *report_descriptor, size_t size, + unsigned short *usage_page, unsigned short *usage) { - return utf8_to_wchar_t(udev_device_get_sysattr_value(dev, udev_name)); -} - -/* uses_numbered_reports() returns 1 if report_descriptor describes a device - which contains numbered reports. */ -static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) { unsigned int i = 0; int size_code; int data_len, key_size; + int usage_found = 0, usage_page_found = 0; while (i < size) { int key = report_descriptor[i]; - - /* Check for the Report ID key */ - if (key == 0x85/*Report ID*/) { - /* This device has a Report ID, which means it uses - numbered reports. */ - return 1; - } + int key_cmd = key & 0xfc; //printf("key: %02hhx\n", key); @@ -191,379 +291,390 @@ static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) { key_size = 1; } + if (key_cmd == 0x4) { + *usage_page = get_bytes(report_descriptor, size, data_len, i); + usage_page_found = 1; + //printf("Usage Page: %x\n", (uint32_t)*usage_page); + } + if (key_cmd == 0x8) { + *usage = get_bytes(report_descriptor, size, data_len, i); + usage_found = 1; + //printf("Usage: %x\n", (uint32_t)*usage); + } + + if (usage_page_found && usage_found) + return 0; /* success */ + /* Skip over this key and it's associated data */ i += data_len + key_size; } - /* Didn't find a Report ID key. Device doesn't use numbered reports. */ + return -1; /* failure */ +} +#endif /* INVASIVE_GET_USAGE */ + +#if defined(__FreeBSD__) && __FreeBSD__ < 10 +/* The libusb version included in FreeBSD < 10 doesn't have this function. In + mainline libusb, it's inlined in libusb.h. This function will bear a striking + resemblance to that one, because there's about one way to code it. + + Note that the data parameter is Unicode in UTF-16LE encoding. + Return value is the number of bytes in data, or LIBUSB_ERROR_*. + */ +static inline int libusb_get_string_descriptor(libusb_device_handle *dev, + uint8_t descriptor_index, uint16_t lang_id, + unsigned char *data, int length) +{ + return libusb_control_transfer(dev, + LIBUSB_ENDPOINT_IN | 0x0, /* Endpoint 0 IN */ + LIBUSB_REQUEST_GET_DESCRIPTOR, + (LIBUSB_DT_STRING << 8) | descriptor_index, + lang_id, data, (uint16_t) length, 1000); +} + +#endif + + +/* Get the first language the device says it reports. This comes from + USB string #0. */ +static uint16_t get_first_language(libusb_device_handle *dev) +{ + uint16_t buf[32]; + int len; + + /* Get the string from libusb. */ + len = libusb_get_string_descriptor(dev, + 0x0, /* String ID */ + 0x0, /* Language */ + (unsigned char*)buf, + sizeof(buf)); + if (len < 4) + return 0x0; + + return buf[1]; /* First two bytes are len and descriptor type. */ +} + +static int is_language_supported(libusb_device_handle *dev, uint16_t lang) +{ + uint16_t buf[32]; + int len; + int i; + + /* Get the string from libusb. */ + len = libusb_get_string_descriptor(dev, + 0x0, /* String ID */ + 0x0, /* Language */ + (unsigned char*)buf, + sizeof(buf)); + if (len < 4) + return 0x0; + + + len /= 2; /* language IDs are two-bytes each. */ + /* Start at index 1 because there are two bytes of protocol data. */ + for (i = 1; i < len; i++) { + if (buf[i] == lang) + return 1; + } + return 0; } -/* - * The caller is responsible for free()ing the (newly-allocated) character - * strings pointed to by serial_number_utf8 and product_name_utf8 after use. - */ -static int -parse_uevent_info(const char *uevent, int *bus_type, - unsigned short *vendor_id, unsigned short *product_id, - char **serial_number_utf8, char **product_name_utf8) + +/* This function returns a newly allocated wide string containing the USB + device string numbered by the index. The returned string must be freed + by using free(). */ +static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx) { - char *tmp = strdup(uevent); - char *saveptr = NULL; - char *line; - char *key; - char *value; + char buf[512]; + int len; + wchar_t *str = NULL; - int found_id = 0; - int found_serial = 0; - int found_name = 0; +#ifndef __ANDROID__ /* we don't use iconv on Android */ + wchar_t wbuf[256]; + /* iconv variables */ + iconv_t ic; + size_t inbytes; + size_t outbytes; + size_t res; +#ifdef __FreeBSD__ + const char *inptr; +#else + char *inptr; +#endif + char *outptr; +#endif - line = strtok_r(tmp, "\n", &saveptr); - while (line != NULL) { - /* line: "KEY=value" */ - key = line; - value = strchr(line, '='); - if (!value) { - goto next_line; - } - *value = '\0'; - value++; + /* Determine which language to use. */ + uint16_t lang; + lang = get_usb_code_for_current_locale(); + if (!is_language_supported(dev, lang)) + lang = get_first_language(dev); - if (strcmp(key, "HID_ID") == 0) { - /** - * type vendor product - * HID_ID=0003:000005AC:00008242 - **/ - int ret = sscanf(value, "%x:%hx:%hx", bus_type, vendor_id, product_id); - if (ret == 3) { - found_id = 1; - } - } else if (strcmp(key, "HID_NAME") == 0) { - /* The caller has to free the product name */ - *product_name_utf8 = strdup(value); - found_name = 1; - } else if (strcmp(key, "HID_UNIQ") == 0) { - /* The caller has to free the serial number */ - *serial_number_utf8 = strdup(value); - found_serial = 1; - } + /* Get the string from libusb. */ + len = libusb_get_string_descriptor(dev, + idx, + lang, + (unsigned char*)buf, + sizeof(buf)); + if (len < 0) + return NULL; -next_line: - line = strtok_r(NULL, "\n", &saveptr); +#ifdef __ANDROID__ + + /* Bionic does not have iconv support nor wcsdup() function, so it + has to be done manually. The following code will only work for + code points that can be represented as a single UTF-16 character, + and will incorrectly convert any code points which require more + than one UTF-16 character. + + Skip over the first character (2-bytes). */ + len -= 2; + str = malloc((len / 2 + 1) * sizeof(wchar_t)); + int i; + for (i = 0; i < len / 2; i++) { + str[i] = buf[i * 2 + 2] | (buf[i * 2 + 3] << 8); + } + str[len / 2] = 0x00000000; + +#else + + /* buf does not need to be explicitly NULL-terminated because + it is only passed into iconv() which does not need it. */ + + /* Initialize iconv. */ + ic = iconv_open("WCHAR_T", "UTF-16LE"); + if (ic == (iconv_t)-1) { + LOG("iconv_open() failed\n"); + return NULL; } - free(tmp); - return (found_id && found_name && found_serial); + /* Convert to native wchar_t (UTF-32 on glibc/BSD systems). + Skip the first character (2-bytes). */ + inptr = buf+2; + inbytes = len-2; + outptr = (char*) wbuf; + outbytes = sizeof(wbuf); + res = iconv(ic, &inptr, &inbytes, &outptr, &outbytes); + if (res == (size_t)-1) { + LOG("iconv() failed\n"); + goto err; + } + + /* Write the terminating NULL. */ + wbuf[sizeof(wbuf)/sizeof(wbuf[0])-1] = 0x00000000; + if (outbytes >= sizeof(wbuf[0])) + *((wchar_t*)outptr) = 0x00000000; + + /* Allocate and copy the string. */ + str = wcsdup(wbuf); + +err: + iconv_close(ic); + +#endif + + return str; } - -static int get_device_string(hid_device *dev, enum device_string_id key, wchar_t *string, size_t maxlen) +static char *make_path(libusb_device *dev, int interface_number) { - struct udev *udev; - struct udev_device *udev_dev, *parent, *hid_dev; - struct stat s; - int ret = -1; - char *serial_number_utf8 = NULL; - char *product_name_utf8 = NULL; + char str[64]; + snprintf(str, sizeof(str), "%04x:%04x:%02x", + libusb_get_bus_number(dev), + libusb_get_device_address(dev), + interface_number); + str[sizeof(str)-1] = '\0'; - /* Create the udev object */ - udev = udev_new(); - if (!udev) { - printf("Can't create udev\n"); - return -1; - } - - /* Get the dev_t (major/minor numbers) from the file handle. */ - ret = fstat(dev->device_handle, &s); - if (-1 == ret) - return ret; - /* Open a udev device from the dev_t. 'c' means character device. */ - udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev); - if (udev_dev) { - hid_dev = udev_device_get_parent_with_subsystem_devtype( - udev_dev, - "hid", - NULL); - if (hid_dev) { - unsigned short dev_vid; - unsigned short dev_pid; - int bus_type; - size_t retm; - - ret = parse_uevent_info( - udev_device_get_sysattr_value(hid_dev, "uevent"), - &bus_type, - &dev_vid, - &dev_pid, - &serial_number_utf8, - &product_name_utf8); - - if (bus_type == BUS_BLUETOOTH) { - switch (key) { - case DEVICE_STRING_MANUFACTURER: - wcsncpy(string, L"", maxlen); - ret = 0; - break; - case DEVICE_STRING_PRODUCT: - retm = mbstowcs(string, product_name_utf8, maxlen); - ret = (retm == (size_t)-1)? -1: 0; - break; - case DEVICE_STRING_SERIAL: - retm = mbstowcs(string, serial_number_utf8, maxlen); - ret = (retm == (size_t)-1)? -1: 0; - break; - case DEVICE_STRING_COUNT: - default: - ret = -1; - break; - } - } - else { - /* This is a USB device. Find its parent USB Device node. */ - parent = udev_device_get_parent_with_subsystem_devtype( - udev_dev, - "usb", - "usb_device"); - if (parent) { - const char *str; - const char *key_str = NULL; - - if (key >= 0 && key < DEVICE_STRING_COUNT) { - key_str = device_string_names[key]; - } else { - ret = -1; - goto end; - } - - str = udev_device_get_sysattr_value(parent, key_str); - if (str) { - /* Convert the string from UTF-8 to wchar_t */ - retm = mbstowcs(string, str, maxlen); - ret = (retm == (size_t)-1)? -1: 0; - goto end; - } - } - } - } - } - -end: - free(serial_number_utf8); - free(product_name_utf8); - - udev_device_unref(udev_dev); - /* parent and hid_dev don't need to be (and can't be) unref'd. - I'm not sure why, but they'll throw double-free() errors. */ - udev_unref(udev); - - return ret; + return strdup(str); } + int HID_API_EXPORT hid_init(void) { - const char *locale; + if (!usb_context) { + const char *locale; - /* Set the locale if it's not set. */ - locale = setlocale(LC_CTYPE, NULL); - if (!locale) - setlocale(LC_CTYPE, ""); + /* Init Libusb */ + if (libusb_init(&usb_context)) + return -1; - kernel_version = detect_kernel_version(); + /* Set the locale if it's not set. */ + locale = setlocale(LC_CTYPE, NULL); + if (!locale) + setlocale(LC_CTYPE, ""); + } return 0; } int HID_API_EXPORT hid_exit(void) { - /* Nothing to do for this in the Linux/hidraw implementation. */ + if (usb_context) { + libusb_exit(usb_context); + usb_context = NULL; + } + return 0; } - struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) { - struct udev *udev; - struct udev_enumerate *enumerate; - struct udev_list_entry *devices, *dev_list_entry; + libusb_device **devs; + libusb_device *dev; + libusb_device_handle *handle; + ssize_t num_devs; + int i = 0; struct hid_device_info *root = NULL; /* return object */ struct hid_device_info *cur_dev = NULL; - struct hid_device_info *prev_dev = NULL; /* previous device */ - hid_init(); - - /* Create the udev object */ - udev = udev_new(); - if (!udev) { - printf("Can't create udev\n"); + if(hid_init() < 0) return NULL; + + num_devs = libusb_get_device_list(usb_context, &devs); + if (num_devs < 0) + return NULL; + while ((dev = devs[i++]) != NULL) { + struct libusb_device_descriptor desc; + struct libusb_config_descriptor *conf_desc = NULL; + int j, k; + int interface_num = 0; + + int res = libusb_get_device_descriptor(dev, &desc); + unsigned short dev_vid = desc.idVendor; + unsigned short dev_pid = desc.idProduct; + + res = libusb_get_active_config_descriptor(dev, &conf_desc); + if (res < 0) + libusb_get_config_descriptor(dev, 0, &conf_desc); + if (conf_desc) { + for (j = 0; j < conf_desc->bNumInterfaces; j++) { + const struct libusb_interface *intf = &conf_desc->interface[j]; + for (k = 0; k < intf->num_altsetting; k++) { + const struct libusb_interface_descriptor *intf_desc; + intf_desc = &intf->altsetting[k]; + if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { + interface_num = intf_desc->bInterfaceNumber; + + /* Check the VID/PID against the arguments */ + if ((vendor_id == 0x0 || vendor_id == dev_vid) && + (product_id == 0x0 || product_id == dev_pid)) { + struct hid_device_info *tmp; + + /* VID/PID match. Create the record. */ + tmp = calloc(1, sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; + + /* Fill out the record */ + cur_dev->next = NULL; + cur_dev->path = make_path(dev, interface_num); + + res = libusb_open(dev, &handle); + + if (res >= 0) { + /* Serial Number */ + if (desc.iSerialNumber > 0) + cur_dev->serial_number = + get_usb_string(handle, desc.iSerialNumber); + + /* Manufacturer and Product strings */ + if (desc.iManufacturer > 0) + cur_dev->manufacturer_string = + get_usb_string(handle, desc.iManufacturer); + if (desc.iProduct > 0) + cur_dev->product_string = + get_usb_string(handle, desc.iProduct); + +#ifdef INVASIVE_GET_USAGE +{ + /* + This section is removed because it is too + invasive on the system. Getting a Usage Page + and Usage requires parsing the HID Report + descriptor. Getting a HID Report descriptor + involves claiming the interface. Claiming the + interface involves detaching the kernel driver. + Detaching the kernel driver is hard on the system + because it will unclaim interfaces (if another + app has them claimed) and the re-attachment of + the driver will sometimes change /dev entry names. + It is for these reasons that this section is + #if 0. For composite devices, use the interface + field in the hid_device_info struct to distinguish + between interfaces. */ + unsigned char data[256]; +#ifdef DETACH_KERNEL_DRIVER + int detached = 0; + /* Usage Page and Usage */ + res = libusb_kernel_driver_active(handle, interface_num); + if (res == 1) { + res = libusb_detach_kernel_driver(handle, interface_num); + if (res < 0) + LOG("Couldn't detach kernel driver, even though a kernel driver was attached."); + else + detached = 1; + } +#endif + res = libusb_claim_interface(handle, interface_num); + if (res >= 0) { + /* Get the HID Report Descriptor. */ + res = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8)|interface_num, 0, data, sizeof(data), 5000); + if (res >= 0) { + unsigned short page=0, usage=0; + /* Parse the usage and usage page + out of the report descriptor. */ + get_usage(data, res, &page, &usage); + cur_dev->usage_page = page; + cur_dev->usage = usage; + } + else + LOG("libusb_control_transfer() for getting the HID report failed with %d\n", res); + + /* Release the interface */ + res = libusb_release_interface(handle, interface_num); + if (res < 0) + LOG("Can't release the interface.\n"); + } + else + LOG("Can't claim interface %d\n", res); +#ifdef DETACH_KERNEL_DRIVER + /* Re-attach kernel driver if necessary. */ + if (detached) { + res = libusb_attach_kernel_driver(handle, interface_num); + if (res < 0) + LOG("Couldn't re-attach kernel driver.\n"); + } +#endif +} +#endif /* INVASIVE_GET_USAGE */ + + libusb_close(handle); + } + /* VID/PID */ + cur_dev->vendor_id = dev_vid; + cur_dev->product_id = dev_pid; + + /* Release Number */ + cur_dev->release_number = desc.bcdDevice; + + /* Interface Number */ + cur_dev->interface_number = interface_num; + } + } + } /* altsettings */ + } /* interfaces */ + libusb_free_config_descriptor(conf_desc); + } } - /* Create a list of the devices in the 'hidraw' subsystem. */ - enumerate = udev_enumerate_new(udev); - udev_enumerate_add_match_subsystem(enumerate, "hidraw"); - udev_enumerate_scan_devices(enumerate); - devices = udev_enumerate_get_list_entry(enumerate); - /* For each item, see if it matches the vid/pid, and if so - create a udev_device record for it */ - udev_list_entry_foreach(dev_list_entry, devices) { - const char *sysfs_path; - const char *dev_path; - const char *str; - struct udev_device *raw_dev; /* The device's hidraw udev node. */ - struct udev_device *hid_dev; /* The device's HID udev node. */ - struct udev_device *usb_dev; /* The device's USB udev node. */ - struct udev_device *intf_dev; /* The device's interface (in the USB sense). */ - unsigned short dev_vid; - unsigned short dev_pid; - char *serial_number_utf8 = NULL; - char *product_name_utf8 = NULL; - int bus_type; - int result; - - /* Get the filename of the /sys entry for the device - and create a udev_device object (dev) representing it */ - sysfs_path = udev_list_entry_get_name(dev_list_entry); - raw_dev = udev_device_new_from_syspath(udev, sysfs_path); - dev_path = udev_device_get_devnode(raw_dev); - - hid_dev = udev_device_get_parent_with_subsystem_devtype( - raw_dev, - "hid", - NULL); - - if (!hid_dev) { - /* Unable to find parent hid device. */ - goto next; - } - - result = parse_uevent_info( - udev_device_get_sysattr_value(hid_dev, "uevent"), - &bus_type, - &dev_vid, - &dev_pid, - &serial_number_utf8, - &product_name_utf8); - - if (!result) { - /* parse_uevent_info() failed for at least one field. */ - goto next; - } - - if (bus_type != BUS_USB && bus_type != BUS_BLUETOOTH) { - /* We only know how to handle USB and BT devices. */ - goto next; - } - - /* Check the VID/PID against the arguments */ - if ((vendor_id == 0x0 || vendor_id == dev_vid) && - (product_id == 0x0 || product_id == dev_pid)) { - struct hid_device_info *tmp; - - /* VID/PID match. Create the record. */ - tmp = malloc(sizeof(struct hid_device_info)); - if (cur_dev) { - cur_dev->next = tmp; - } - else { - root = tmp; - } - prev_dev = cur_dev; - cur_dev = tmp; - - /* Fill out the record */ - cur_dev->next = NULL; - cur_dev->path = dev_path? strdup(dev_path): NULL; - - /* VID/PID */ - cur_dev->vendor_id = dev_vid; - cur_dev->product_id = dev_pid; - - /* Serial Number */ - cur_dev->serial_number = utf8_to_wchar_t(serial_number_utf8); - - /* Release Number */ - cur_dev->release_number = 0x0; - - /* Interface Number */ - cur_dev->interface_number = -1; - - switch (bus_type) { - case BUS_USB: - /* The device pointed to by raw_dev contains information about - the hidraw device. In order to get information about the - USB device, get the parent device with the - subsystem/devtype pair of "usb"/"usb_device". This will - be several levels up the tree, but the function will find - it. */ - usb_dev = udev_device_get_parent_with_subsystem_devtype( - raw_dev, - "usb", - "usb_device"); - - if (!usb_dev) { - /* Free this device */ - free(cur_dev->serial_number); - free(cur_dev->path); - free(cur_dev); - - /* Take it off the device list. */ - if (prev_dev) { - prev_dev->next = NULL; - cur_dev = prev_dev; - } - else { - cur_dev = root = NULL; - } - - goto next; - } - - /* Manufacturer and Product strings */ - cur_dev->manufacturer_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_MANUFACTURER]); - cur_dev->product_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_PRODUCT]); - - /* Release Number */ - str = udev_device_get_sysattr_value(usb_dev, "bcdDevice"); - cur_dev->release_number = (str)? strtol(str, NULL, 16): 0x0; - - /* Get a handle to the interface's udev node. */ - intf_dev = udev_device_get_parent_with_subsystem_devtype( - raw_dev, - "usb", - "usb_interface"); - if (intf_dev) { - str = udev_device_get_sysattr_value(intf_dev, "bInterfaceNumber"); - cur_dev->interface_number = (str)? strtol(str, NULL, 16): -1; - } - - break; - - case BUS_BLUETOOTH: - /* Manufacturer and Product strings */ - cur_dev->manufacturer_string = wcsdup(L""); - cur_dev->product_string = utf8_to_wchar_t(product_name_utf8); - - break; - - default: - /* Unknown device type - this should never happen, as we - * check for USB and Bluetooth devices above */ - break; - } - } - - next: - free(serial_number_utf8); - free(product_name_utf8); - udev_device_unref(raw_dev); - /* hid_dev, usb_dev and intf_dev don't need to be (and can't be) - unref()d. It will cause a double-free() error. I'm not - sure why. */ - } - /* Free the enumerator and udev objects. */ - udev_enumerate_unref(enumerate); - udev_unref(udev); + libusb_free_device_list(devs, 1); return root; } @@ -594,7 +705,8 @@ hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const if (cur_dev->vendor_id == vendor_id && cur_dev->product_id == product_id) { if (serial_number) { - if (wcscmp(serial_number, cur_dev->serial_number) == 0) { + if (cur_dev->serial_number && + wcscmp(serial_number, cur_dev->serial_number) == 0) { path_to_open = cur_dev->path; break; } @@ -617,49 +729,272 @@ hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const return handle; } +static void read_callback(struct libusb_transfer *transfer) +{ + hid_device *dev = transfer->user_data; + int res; + + if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { + + struct input_report *rpt = malloc(sizeof(*rpt)); + rpt->data = malloc(transfer->actual_length); + memcpy(rpt->data, transfer->buffer, transfer->actual_length); + rpt->len = transfer->actual_length; + rpt->next = NULL; + + pthread_mutex_lock(&dev->mutex); + + /* Attach the new report object to the end of the list. */ + if (dev->input_reports == NULL) { + /* The list is empty. Put it at the root. */ + dev->input_reports = rpt; + pthread_cond_signal(&dev->condition); + } + else { + /* Find the end of the list and attach. */ + struct input_report *cur = dev->input_reports; + int num_queued = 0; + while (cur->next != NULL) { + cur = cur->next; + num_queued++; + } + cur->next = rpt; + + /* Pop one off if we've reached 30 in the queue. This + way we don't grow forever if the user never reads + anything from the device. */ + if (num_queued > 30) { + return_data(dev, NULL, 0); + } + } + pthread_mutex_unlock(&dev->mutex); + } + else if (transfer->status == LIBUSB_TRANSFER_CANCELLED) { + dev->shutdown_thread = 1; + dev->cancelled = 1; + return; + } + else if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) { + dev->shutdown_thread = 1; + dev->cancelled = 1; + return; + } + else if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) { + //LOG("Timeout (normal)\n"); + } + else { + LOG("Unknown transfer code: %d\n", transfer->status); + } + + /* Re-submit the transfer object. */ + res = libusb_submit_transfer(transfer); + if (res != 0) { + LOG("Unable to submit URB. libusb error code: %d\n", res); + dev->shutdown_thread = 1; + dev->cancelled = 1; + } +} + + +static void *read_thread(void *param) +{ + hid_device *dev = param; + unsigned char *buf; + const size_t length = dev->input_ep_max_packet_size; + + /* Set up the transfer object. */ + buf = malloc(length); + dev->transfer = libusb_alloc_transfer(0); + libusb_fill_interrupt_transfer(dev->transfer, + dev->device_handle, + dev->input_endpoint, + buf, + length, + read_callback, + dev, + 5000/*timeout*/); + + /* Make the first submission. Further submissions are made + from inside read_callback() */ + libusb_submit_transfer(dev->transfer); + + /* Notify the main thread that the read thread is up and running. */ + pthread_barrier_wait(&dev->barrier); + + /* Handle all the events. */ + while (!dev->shutdown_thread) { + int res; + res = libusb_handle_events(usb_context); + if (res < 0) { + /* There was an error. */ + LOG("read_thread(): libusb reports error # %d\n", res); + + /* Break out of this loop only on fatal error.*/ + if (res != LIBUSB_ERROR_BUSY && + res != LIBUSB_ERROR_TIMEOUT && + res != LIBUSB_ERROR_OVERFLOW && + res != LIBUSB_ERROR_INTERRUPTED) { + break; + } + } + } + + /* Cancel any transfer that may be pending. This call will fail + if no transfers are pending, but that's OK. */ + libusb_cancel_transfer(dev->transfer); + + while (!dev->cancelled) + libusb_handle_events_completed(usb_context, &dev->cancelled); + + /* Now that the read thread is stopping, Wake any threads which are + waiting on data (in hid_read_timeout()). Do this under a mutex to + make sure that a thread which is about to go to sleep waiting on + the condition actually will go to sleep before the condition is + signaled. */ + pthread_mutex_lock(&dev->mutex); + pthread_cond_broadcast(&dev->condition); + pthread_mutex_unlock(&dev->mutex); + + /* The dev->transfer->buffer and dev->transfer objects are cleaned up + in hid_close(). They are not cleaned up here because this thread + could end either due to a disconnect or due to a user + call to hid_close(). In both cases the objects can be safely + cleaned up after the call to pthread_join() (in hid_close()), but + since hid_close() calls libusb_cancel_transfer(), on these objects, + they can not be cleaned up here. */ + + return NULL; +} + + hid_device * HID_API_EXPORT hid_open_path(const char *path) { hid_device *dev = NULL; - hid_init(); + libusb_device **devs; + libusb_device *usb_dev; + int res; + int d = 0; + int good_open = 0; + + if(hid_init() < 0) + return NULL; dev = new_hid_device(); - /* OPEN HERE */ - dev->device_handle = open(path, O_RDWR); + libusb_get_device_list(usb_context, &devs); + while ((usb_dev = devs[d++]) != NULL) { + struct libusb_device_descriptor desc; + struct libusb_config_descriptor *conf_desc = NULL; + int i,j,k; + libusb_get_device_descriptor(usb_dev, &desc); + + if (libusb_get_active_config_descriptor(usb_dev, &conf_desc) < 0) + continue; + for (j = 0; j < conf_desc->bNumInterfaces; j++) { + const struct libusb_interface *intf = &conf_desc->interface[j]; + for (k = 0; k < intf->num_altsetting; k++) { + const struct libusb_interface_descriptor *intf_desc; + intf_desc = &intf->altsetting[k]; + if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { + char *dev_path = make_path(usb_dev, intf_desc->bInterfaceNumber); + if (!strcmp(dev_path, path)) { + /* Matched Paths. Open this device */ + + /* OPEN HERE */ + res = libusb_open(usb_dev, &dev->device_handle); + if (res < 0) { + LOG("can't open device\n"); + free(dev_path); + break; + } + good_open = 1; +#ifdef DETACH_KERNEL_DRIVER + /* Detach the kernel driver, but only if the + device is managed by the kernel */ + if (libusb_kernel_driver_active(dev->device_handle, intf_desc->bInterfaceNumber) == 1) { + res = libusb_detach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber); + if (res < 0) { + libusb_close(dev->device_handle); + LOG("Unable to detach Kernel Driver\n"); + free(dev_path); + good_open = 0; + break; + } + } +#endif + res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber); + if (res < 0) { + LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res); + free(dev_path); + libusb_close(dev->device_handle); + good_open = 0; + break; + } + + /* Store off the string descriptor indexes */ + dev->manufacturer_index = desc.iManufacturer; + dev->product_index = desc.iProduct; + dev->serial_index = desc.iSerialNumber; + + /* Store off the interface number */ + dev->interface = intf_desc->bInterfaceNumber; + + /* Find the INPUT and OUTPUT endpoints. An + OUTPUT endpoint is not required. */ + for (i = 0; i < intf_desc->bNumEndpoints; i++) { + const struct libusb_endpoint_descriptor *ep + = &intf_desc->endpoint[i]; + + /* Determine the type and direction of this + endpoint. */ + int is_interrupt = + (ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) + == LIBUSB_TRANSFER_TYPE_INTERRUPT; + int is_output = + (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) + == LIBUSB_ENDPOINT_OUT; + int is_input = + (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) + == LIBUSB_ENDPOINT_IN; + + /* Decide whether to use it for input or output. */ + if (dev->input_endpoint == 0 && + is_interrupt && is_input) { + /* Use this endpoint for INPUT */ + dev->input_endpoint = ep->bEndpointAddress; + dev->input_ep_max_packet_size = ep->wMaxPacketSize; + } + if (dev->output_endpoint == 0 && + is_interrupt && is_output) { + /* Use this endpoint for OUTPUT */ + dev->output_endpoint = ep->bEndpointAddress; + } + } + + pthread_create(&dev->thread, NULL, read_thread, dev); + + /* Wait here for the read thread to be initialized. */ + pthread_barrier_wait(&dev->barrier); + + } + free(dev_path); + } + } + } + libusb_free_config_descriptor(conf_desc); + + } + + libusb_free_device_list(devs, 1); /* If we have a good handle, return it. */ - if (dev->device_handle > 0) { - - /* Get the report descriptor */ - int res, desc_size = 0; - struct hidraw_report_descriptor rpt_desc; - - memset(&rpt_desc, 0x0, sizeof(rpt_desc)); - - /* Get Report Descriptor Size */ - res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size); - if (res < 0) - perror("HIDIOCGRDESCSIZE"); - - - /* Get Report Descriptor */ - rpt_desc.size = desc_size; - res = ioctl(dev->device_handle, HIDIOCGRDESC, &rpt_desc); - if (res < 0) { - perror("HIDIOCGRDESC"); - } else { - /* Determine if this device uses numbered reports. */ - dev->uses_numbered_reports = - uses_numbered_reports(rpt_desc.value, - rpt_desc.size); - } - + if (good_open) { return dev; } else { /* Unable to open any devices. */ - free(dev); + free_hid_device(dev); return NULL; } } @@ -667,95 +1002,231 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) { - int bytes_written; + int res; + int report_number = data[0]; + int skipped_report_id = 0; - bytes_written = write(dev->device_handle, data, length); + if (report_number == 0x0) { + data++; + length--; + skipped_report_id = 1; + } - return bytes_written; + + if (dev->output_endpoint <= 0) { + /* No interrupt out endpoint. Use the Control Endpoint */ + res = libusb_control_transfer(dev->device_handle, + LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT, + 0x09/*HID Set_Report*/, + (2/*HID output*/ << 8) | report_number, + dev->interface, + (unsigned char *)data, length, + 1000/*timeout millis*/); + + if (res < 0) + return -1; + + if (skipped_report_id) + length++; + + return length; + } + else { + /* Use the interrupt out endpoint */ + int actual_length; + res = libusb_interrupt_transfer(dev->device_handle, + dev->output_endpoint, + (unsigned char*)data, + length, + &actual_length, 1000); + + if (res < 0) + return -1; + + if (skipped_report_id) + actual_length++; + + return actual_length; + } +} + +/* Helper function, to simplify hid_read(). + This should be called with dev->mutex locked. */ +static int return_data(hid_device *dev, unsigned char *data, size_t length) +{ + /* Copy the data out of the linked list item (rpt) into the + return buffer (data), and delete the liked list item. */ + struct input_report *rpt = dev->input_reports; + size_t len = (length < rpt->len)? length: rpt->len; + if (len > 0) + memcpy(data, rpt->data, len); + dev->input_reports = rpt->next; + free(rpt->data); + free(rpt); + return len; +} + +static void cleanup_mutex(void *param) +{ + hid_device *dev = param; + pthread_mutex_unlock(&dev->mutex); } int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) { - int bytes_read; + int bytes_read = -1; - if (milliseconds >= 0) { - /* Milliseconds is either 0 (non-blocking) or > 0 (contains - a valid timeout). In both cases we want to call poll() - and wait for data to arrive. Don't rely on non-blocking - operation (O_NONBLOCK) since some kernels don't seem to - properly report device disconnection through read() when - in non-blocking mode. */ - int ret; - struct pollfd fds; +#if 0 + int transferred; + int res = libusb_interrupt_transfer(dev->device_handle, dev->input_endpoint, data, length, &transferred, 5000); + LOG("transferred: %d\n", transferred); + return transferred; +#endif - fds.fd = dev->device_handle; - fds.events = POLLIN; - fds.revents = 0; - ret = poll(&fds, 1, milliseconds); - if (ret == -1 || ret == 0) { - /* Error or timeout */ - return ret; - } - else { - /* Check for errors on the file descriptor. This will - indicate a device disconnection. */ - if (fds.revents & (POLLERR | POLLHUP | POLLNVAL)) - return -1; - } + pthread_mutex_lock(&dev->mutex); + pthread_cleanup_push(&cleanup_mutex, dev); + + /* There's an input report queued up. Return it. */ + if (dev->input_reports) { + /* Return the first one */ + bytes_read = return_data(dev, data, length); + goto ret; } - bytes_read = read(dev->device_handle, data, length); - if (bytes_read < 0 && (errno == EAGAIN || errno == EINPROGRESS)) + if (dev->shutdown_thread) { + /* This means the device has been disconnected. + An error code of -1 should be returned. */ + bytes_read = -1; + goto ret; + } + + if (milliseconds == -1) { + /* Blocking */ + while (!dev->input_reports && !dev->shutdown_thread) { + pthread_cond_wait(&dev->condition, &dev->mutex); + } + if (dev->input_reports) { + bytes_read = return_data(dev, data, length); + } + } + else if (milliseconds > 0) { + /* Non-blocking, but called with timeout. */ + int res; + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += milliseconds / 1000; + ts.tv_nsec += (milliseconds % 1000) * 1000000; + if (ts.tv_nsec >= 1000000000L) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000L; + } + + while (!dev->input_reports && !dev->shutdown_thread) { + res = pthread_cond_timedwait(&dev->condition, &dev->mutex, &ts); + if (res == 0) { + if (dev->input_reports) { + bytes_read = return_data(dev, data, length); + break; + } + + /* If we're here, there was a spurious wake up + or the read thread was shutdown. Run the + loop again (ie: don't break). */ + } + else if (res == ETIMEDOUT) { + /* Timed out. */ + bytes_read = 0; + break; + } + else { + /* Error. */ + bytes_read = -1; + break; + } + } + } + else { + /* Purely non-blocking */ bytes_read = 0; - - if (bytes_read >= 0 && - kernel_version != 0 && - kernel_version < KERNEL_VERSION(2,6,34) && - dev->uses_numbered_reports) { - /* Work around a kernel bug. Chop off the first byte. */ - memmove(data, data+1, bytes_read); - bytes_read--; } +ret: + pthread_mutex_unlock(&dev->mutex); + pthread_cleanup_pop(0); + return bytes_read; } int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) { - return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); + return hid_read_timeout(dev, data, length, dev->blocking ? -1 : 0); } int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) { - /* Do all non-blocking in userspace using poll(), since it looks - like there's a bug in the kernel in some versions where - read() will not return -1 on disconnection of the USB device */ - dev->blocking = !nonblock; - return 0; /* Success */ + + return 0; } int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) { - int res; + int res = -1; + int skipped_report_id = 0; + int report_number = data[0]; + + if (report_number == 0x0) { + data++; + length--; + skipped_report_id = 1; + } + + res = libusb_control_transfer(dev->device_handle, + LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT, + 0x09/*HID set_report*/, + (3/*HID feature*/ << 8) | report_number, + dev->interface, + (unsigned char *)data, length, + 1000/*timeout millis*/); - res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data); if (res < 0) - perror("ioctl (SFEATURE)"); + return -1; - return res; + /* Account for the report ID */ + if (skipped_report_id) + length++; + + return length; } int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) { - int res; + int res = -1; + int skipped_report_id = 0; + int report_number = data[0]; + + if (report_number == 0x0) { + /* Offset the return buffer by 1, so that the report ID + will remain in byte 0. */ + data++; + length--; + skipped_report_id = 1; + } + res = libusb_control_transfer(dev->device_handle, + LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_IN, + 0x01/*HID get_report*/, + (3/*HID feature*/ << 8) | report_number, + dev->interface, + (unsigned char *)data, length, + 1000/*timeout millis*/); - res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data); if (res < 0) - perror("ioctl (GFEATURE)"); + return -1; + if (skipped_report_id) + res++; return res; } @@ -765,29 +1236,63 @@ void HID_API_EXPORT hid_close(hid_device *dev) { if (!dev) return; - close(dev->device_handle); - free(dev); + + /* Cause read_thread() to stop. */ + dev->shutdown_thread = 1; + libusb_cancel_transfer(dev->transfer); + + /* Wait for read_thread() to end. */ + pthread_join(dev->thread, NULL); + + /* Clean up the Transfer objects allocated in read_thread(). */ + free(dev->transfer->buffer); + libusb_free_transfer(dev->transfer); + + /* release the interface */ + libusb_release_interface(dev->device_handle, dev->interface); + + /* Close the handle */ + libusb_close(dev->device_handle); + + /* Clear out the queue of received reports. */ + pthread_mutex_lock(&dev->mutex); + while (dev->input_reports) { + return_data(dev, NULL, 0); + } + pthread_mutex_unlock(&dev->mutex); + + free_hid_device(dev); } int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) { - return get_device_string(dev, DEVICE_STRING_MANUFACTURER, string, maxlen); + return hid_get_indexed_string(dev, dev->manufacturer_index, string, maxlen); } int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) { - return get_device_string(dev, DEVICE_STRING_PRODUCT, string, maxlen); + return hid_get_indexed_string(dev, dev->product_index, string, maxlen); } int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) { - return get_device_string(dev, DEVICE_STRING_SERIAL, string, maxlen); + return hid_get_indexed_string(dev, dev->serial_index, string, maxlen); } int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) { - return -1; + wchar_t *str; + + str = get_usb_string(dev->device_handle, string_index); + if (str) { + wcsncpy(string, str, maxlen); + string[maxlen-1] = L'\0'; + free(str); + return 0; + } + else + return -1; } @@ -795,3 +1300,215 @@ HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) { return NULL; } + + +struct lang_map_entry { + const char *name; + const char *string_code; + uint16_t usb_code; +}; + +#define LANG(name,code,usb_code) { name, code, usb_code } +static struct lang_map_entry lang_map[] = { + LANG("Afrikaans", "af", 0x0436), + LANG("Albanian", "sq", 0x041C), + LANG("Arabic - United Arab Emirates", "ar_ae", 0x3801), + LANG("Arabic - Bahrain", "ar_bh", 0x3C01), + LANG("Arabic - Algeria", "ar_dz", 0x1401), + LANG("Arabic - Egypt", "ar_eg", 0x0C01), + LANG("Arabic - Iraq", "ar_iq", 0x0801), + LANG("Arabic - Jordan", "ar_jo", 0x2C01), + LANG("Arabic - Kuwait", "ar_kw", 0x3401), + LANG("Arabic - Lebanon", "ar_lb", 0x3001), + LANG("Arabic - Libya", "ar_ly", 0x1001), + LANG("Arabic - Morocco", "ar_ma", 0x1801), + LANG("Arabic - Oman", "ar_om", 0x2001), + LANG("Arabic - Qatar", "ar_qa", 0x4001), + LANG("Arabic - Saudi Arabia", "ar_sa", 0x0401), + LANG("Arabic - Syria", "ar_sy", 0x2801), + LANG("Arabic - Tunisia", "ar_tn", 0x1C01), + LANG("Arabic - Yemen", "ar_ye", 0x2401), + LANG("Armenian", "hy", 0x042B), + LANG("Azeri - Latin", "az_az", 0x042C), + LANG("Azeri - Cyrillic", "az_az", 0x082C), + LANG("Basque", "eu", 0x042D), + LANG("Belarusian", "be", 0x0423), + LANG("Bulgarian", "bg", 0x0402), + LANG("Catalan", "ca", 0x0403), + LANG("Chinese - China", "zh_cn", 0x0804), + LANG("Chinese - Hong Kong SAR", "zh_hk", 0x0C04), + LANG("Chinese - Macau SAR", "zh_mo", 0x1404), + LANG("Chinese - Singapore", "zh_sg", 0x1004), + LANG("Chinese - Taiwan", "zh_tw", 0x0404), + LANG("Croatian", "hr", 0x041A), + LANG("Czech", "cs", 0x0405), + LANG("Danish", "da", 0x0406), + LANG("Dutch - Netherlands", "nl_nl", 0x0413), + LANG("Dutch - Belgium", "nl_be", 0x0813), + LANG("English - Australia", "en_au", 0x0C09), + LANG("English - Belize", "en_bz", 0x2809), + LANG("English - Canada", "en_ca", 0x1009), + LANG("English - Caribbean", "en_cb", 0x2409), + LANG("English - Ireland", "en_ie", 0x1809), + LANG("English - Jamaica", "en_jm", 0x2009), + LANG("English - New Zealand", "en_nz", 0x1409), + LANG("English - Phillippines", "en_ph", 0x3409), + LANG("English - Southern Africa", "en_za", 0x1C09), + LANG("English - Trinidad", "en_tt", 0x2C09), + LANG("English - Great Britain", "en_gb", 0x0809), + LANG("English - United States", "en_us", 0x0409), + LANG("Estonian", "et", 0x0425), + LANG("Farsi", "fa", 0x0429), + LANG("Finnish", "fi", 0x040B), + LANG("Faroese", "fo", 0x0438), + LANG("French - France", "fr_fr", 0x040C), + LANG("French - Belgium", "fr_be", 0x080C), + LANG("French - Canada", "fr_ca", 0x0C0C), + LANG("French - Luxembourg", "fr_lu", 0x140C), + LANG("French - Switzerland", "fr_ch", 0x100C), + LANG("Gaelic - Ireland", "gd_ie", 0x083C), + LANG("Gaelic - Scotland", "gd", 0x043C), + LANG("German - Germany", "de_de", 0x0407), + LANG("German - Austria", "de_at", 0x0C07), + LANG("German - Liechtenstein", "de_li", 0x1407), + LANG("German - Luxembourg", "de_lu", 0x1007), + LANG("German - Switzerland", "de_ch", 0x0807), + LANG("Greek", "el", 0x0408), + LANG("Hebrew", "he", 0x040D), + LANG("Hindi", "hi", 0x0439), + LANG("Hungarian", "hu", 0x040E), + LANG("Icelandic", "is", 0x040F), + LANG("Indonesian", "id", 0x0421), + LANG("Italian - Italy", "it_it", 0x0410), + LANG("Italian - Switzerland", "it_ch", 0x0810), + LANG("Japanese", "ja", 0x0411), + LANG("Korean", "ko", 0x0412), + LANG("Latvian", "lv", 0x0426), + LANG("Lithuanian", "lt", 0x0427), + LANG("F.Y.R.O. Macedonia", "mk", 0x042F), + LANG("Malay - Malaysia", "ms_my", 0x043E), + LANG("Malay – Brunei", "ms_bn", 0x083E), + LANG("Maltese", "mt", 0x043A), + LANG("Marathi", "mr", 0x044E), + LANG("Norwegian - Bokml", "no_no", 0x0414), + LANG("Norwegian - Nynorsk", "no_no", 0x0814), + LANG("Polish", "pl", 0x0415), + LANG("Portuguese - Portugal", "pt_pt", 0x0816), + LANG("Portuguese - Brazil", "pt_br", 0x0416), + LANG("Raeto-Romance", "rm", 0x0417), + LANG("Romanian - Romania", "ro", 0x0418), + LANG("Romanian - Republic of Moldova", "ro_mo", 0x0818), + LANG("Russian", "ru", 0x0419), + LANG("Russian - Republic of Moldova", "ru_mo", 0x0819), + LANG("Sanskrit", "sa", 0x044F), + LANG("Serbian - Cyrillic", "sr_sp", 0x0C1A), + LANG("Serbian - Latin", "sr_sp", 0x081A), + LANG("Setsuana", "tn", 0x0432), + LANG("Slovenian", "sl", 0x0424), + LANG("Slovak", "sk", 0x041B), + LANG("Sorbian", "sb", 0x042E), + LANG("Spanish - Spain (Traditional)", "es_es", 0x040A), + LANG("Spanish - Argentina", "es_ar", 0x2C0A), + LANG("Spanish - Bolivia", "es_bo", 0x400A), + LANG("Spanish - Chile", "es_cl", 0x340A), + LANG("Spanish - Colombia", "es_co", 0x240A), + LANG("Spanish - Costa Rica", "es_cr", 0x140A), + LANG("Spanish - Dominican Republic", "es_do", 0x1C0A), + LANG("Spanish - Ecuador", "es_ec", 0x300A), + LANG("Spanish - Guatemala", "es_gt", 0x100A), + LANG("Spanish - Honduras", "es_hn", 0x480A), + LANG("Spanish - Mexico", "es_mx", 0x080A), + LANG("Spanish - Nicaragua", "es_ni", 0x4C0A), + LANG("Spanish - Panama", "es_pa", 0x180A), + LANG("Spanish - Peru", "es_pe", 0x280A), + LANG("Spanish - Puerto Rico", "es_pr", 0x500A), + LANG("Spanish - Paraguay", "es_py", 0x3C0A), + LANG("Spanish - El Salvador", "es_sv", 0x440A), + LANG("Spanish - Uruguay", "es_uy", 0x380A), + LANG("Spanish - Venezuela", "es_ve", 0x200A), + LANG("Southern Sotho", "st", 0x0430), + LANG("Swahili", "sw", 0x0441), + LANG("Swedish - Sweden", "sv_se", 0x041D), + LANG("Swedish - Finland", "sv_fi", 0x081D), + LANG("Tamil", "ta", 0x0449), + LANG("Tatar", "tt", 0X0444), + LANG("Thai", "th", 0x041E), + LANG("Turkish", "tr", 0x041F), + LANG("Tsonga", "ts", 0x0431), + LANG("Ukrainian", "uk", 0x0422), + LANG("Urdu", "ur", 0x0420), + LANG("Uzbek - Cyrillic", "uz_uz", 0x0843), + LANG("Uzbek – Latin", "uz_uz", 0x0443), + LANG("Vietnamese", "vi", 0x042A), + LANG("Xhosa", "xh", 0x0434), + LANG("Yiddish", "yi", 0x043D), + LANG("Zulu", "zu", 0x0435), + LANG(NULL, NULL, 0x0), +}; + +uint16_t get_usb_code_for_current_locale(void) +{ + char *locale; + char search_string[64]; + char *ptr; + struct lang_map_entry *lang; + + /* Get the current locale. */ + locale = setlocale(0, NULL); + if (!locale) + return 0x0; + + /* Make a copy of the current locale string. */ + strncpy(search_string, locale, sizeof(search_string)); + search_string[sizeof(search_string)-1] = '\0'; + + /* Chop off the encoding part, and make it lower case. */ + ptr = search_string; + while (*ptr) { + *ptr = tolower(*ptr); + if (*ptr == '.') { + *ptr = '\0'; + break; + } + ptr++; + } + + /* Find the entry which matches the string code of our locale. */ + lang = lang_map; + while (lang->string_code) { + if (!strcmp(lang->string_code, search_string)) { + return lang->usb_code; + } + lang++; + } + + /* There was no match. Find with just the language only. */ + /* Chop off the variant. Chop it off at the '_'. */ + ptr = search_string; + while (*ptr) { + *ptr = tolower(*ptr); + if (*ptr == '_') { + *ptr = '\0'; + break; + } + ptr++; + } + +#if 0 /* TODO: Do we need this? */ + /* Find the entry which matches the string code of our language. */ + lang = lang_map; + while (lang->string_code) { + if (!strcmp(lang->string_code, search_string)) { + return lang->usb_code; + } + lang++; + } +#endif + + /* Found nothing. */ + return 0x0; +} + +#ifdef __cplusplus +} +#endif From 1aa559585ca210740edbcd7027de22b7affc7b82 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 27 Sep 2019 15:26:13 +0200 Subject: [PATCH 03/73] Added missing include --- src/slic3r/GUI/Mouse3DController.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index b62a00b6f..c4b3f78e4 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -2,8 +2,10 @@ #define slic3r_Mouse3DController_hpp_ #if ENABLE_3DCONNEXION_DEVICES +#include "libslic3r/Point.hpp" #include "hidapi/hidapi.h" + #include #include #include From 82fed1790a5ec9f0803aaa70ecce719c636a1e2c Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Mon, 30 Sep 2019 14:58:51 +0200 Subject: [PATCH 04/73] 3D connexion support on Linux: - Replace hidapi/linux/hid.c with the hidraw variant (link to udev) - Add CMakeLists.txt for hidapi, refactor - Add udev rules file (no installation so far) --- CMakeLists.txt | 19 - resources/udev/90-3dconnexion.rules | 23 + src/CMakeLists.txt | 1 + src/hidapi/CMakeLists.txt | 17 + src/hidapi/{ => include}/hidapi.h | 0 src/hidapi/linux/hid.c | 1717 ++++++++------------------ src/slic3r/CMakeLists.txt | 2 +- src/slic3r/GUI/Mouse3DController.cpp | 2 + 8 files changed, 544 insertions(+), 1237 deletions(-) create mode 100644 resources/udev/90-3dconnexion.rules create mode 100644 src/hidapi/CMakeLists.txt rename src/hidapi/{ => include}/hidapi.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index b9a7cf065..8c7fc12df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -365,25 +365,6 @@ include_directories(${GLEW_INCLUDE_DIRS}) add_library(cereal INTERFACE) target_include_directories(cereal INTERFACE include) -# Find the hidapi library -if(WIN32) -add_library(hidapi STATIC - ${LIBDIR}/hidapi/win/hid.c -) -elseif (APPLE) -add_library(hidapi STATIC - ${LIBDIR}/hidapi/mac/hid.c -) -else () -add_library(hidapi STATIC - ${LIBDIR}/hidapi/linux/hid.c -) -endif () -set(HIDAPI_FOUND 1) -set(HIDAPI_INCLUDE_DIRS ${LIBDIR}/hidapi/) -set(HIDAPI_LIBRARIES hidapi) -include_directories(${HIDAPI_INCLUDE_DIRS}) - # l10n set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization") add_custom_target(pot diff --git a/resources/udev/90-3dconnexion.rules b/resources/udev/90-3dconnexion.rules new file mode 100644 index 000000000..035d17125 --- /dev/null +++ b/resources/udev/90-3dconnexion.rules @@ -0,0 +1,23 @@ +# See src/slic3r/GUI/Mouse3DController.cpp for the list of devices + +# Logitech vendor devices +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c623", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c626", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c628", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c627", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c603", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62b", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c621", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c625", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c629", MODE="0666" + +# 3D Connexion vendor devices +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c623", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c626", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c628", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c627", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c603", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62b", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c621", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c625", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c629", MODE="0666" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 31cb24f24..dc1d5e38c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,6 +14,7 @@ add_subdirectory(qhull) add_subdirectory(Shiny) add_subdirectory(semver) add_subdirectory(libigl) +add_subdirectory(hidapi) # Adding libnest2d project for bin packing... set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d") diff --git a/src/hidapi/CMakeLists.txt b/src/hidapi/CMakeLists.txt new file mode 100644 index 000000000..313fafbfd --- /dev/null +++ b/src/hidapi/CMakeLists.txt @@ -0,0 +1,17 @@ + +if (WIN32) + set(HIDAPI_IMPL win/hid.c) +elseif (APPLE) + set(HIDAPI_IMPL mac/hid.c) +else () + # Assume Linux or Unix other than Mac OS + set(HIDAPI_IMPL linux/hid.c) +endif() + +include_directories(include) + +add_library(hidapi STATIC ${HIDAPI_IMPL}) + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_link_libraries(hidapi udev) +endif() diff --git a/src/hidapi/hidapi.h b/src/hidapi/include/hidapi.h similarity index 100% rename from src/hidapi/hidapi.h rename to src/hidapi/include/hidapi.h diff --git a/src/hidapi/linux/hid.c b/src/hidapi/linux/hid.c index 8cd5e3dca..56dac0fab 100644 --- a/src/hidapi/linux/hid.c +++ b/src/hidapi/linux/hid.c @@ -6,9 +6,7 @@ Signal 11 Software 8/22/2009 - Linux Version - 6/2/2010 - Libusb Version - 8/13/2010 - FreeBSD Version - 11/1/2011 + Linux Version - 6/2/2009 Copyright 2009, All Rights Reserved. @@ -23,13 +21,10 @@ http://github.com/signal11/hidapi . ********************************************************/ -#define _GNU_SOURCE /* needed for wcsdup() before glibc 2.10 */ - /* C */ #include #include #include -#include #include #include @@ -40,219 +35,124 @@ #include #include #include -#include -#include +#include -/* GNU / LibUSB */ -#include -#ifndef __ANDROID__ -#include -#endif +/* Linux */ +#include +#include +#include +#include #include "hidapi.h" -#ifdef __ANDROID__ - -/* Barrier implementation because Android/Bionic don't have pthread_barrier. - This implementation came from Brent Priddy and was posted on - StackOverflow. It is used with his permission. */ -typedef int pthread_barrierattr_t; -typedef struct pthread_barrier { - pthread_mutex_t mutex; - pthread_cond_t cond; - int count; - int trip_count; -} pthread_barrier_t; - -static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) -{ - if(count == 0) { - errno = EINVAL; - return -1; - } - - if(pthread_mutex_init(&barrier->mutex, 0) < 0) { - return -1; - } - if(pthread_cond_init(&barrier->cond, 0) < 0) { - pthread_mutex_destroy(&barrier->mutex); - return -1; - } - barrier->trip_count = count; - barrier->count = 0; - - return 0; -} - -static int pthread_barrier_destroy(pthread_barrier_t *barrier) -{ - pthread_cond_destroy(&barrier->cond); - pthread_mutex_destroy(&barrier->mutex); - return 0; -} - -static int pthread_barrier_wait(pthread_barrier_t *barrier) -{ - pthread_mutex_lock(&barrier->mutex); - ++(barrier->count); - if(barrier->count >= barrier->trip_count) - { - barrier->count = 0; - pthread_cond_broadcast(&barrier->cond); - pthread_mutex_unlock(&barrier->mutex); - return 1; - } - else - { - pthread_cond_wait(&barrier->cond, &(barrier->mutex)); - pthread_mutex_unlock(&barrier->mutex); - return 0; - } -} - +/* Definitions from linux/hidraw.h. Since these are new, some distros + may not have header files which contain them. */ +#ifndef HIDIOCSFEATURE +#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len) +#endif +#ifndef HIDIOCGFEATURE +#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len) #endif -#ifdef __cplusplus -extern "C" { -#endif -#ifdef DEBUG_PRINTF -#define LOG(...) fprintf(stderr, __VA_ARGS__) -#else -#define LOG(...) do {} while (0) -#endif - -#ifndef __FreeBSD__ -#define DETACH_KERNEL_DRIVER -#endif - -/* Uncomment to enable the retrieval of Usage and Usage Page in -hid_enumerate(). Warning, on platforms different from FreeBSD -this is very invasive as it requires the detach -and re-attach of the kernel driver. See comments inside hid_enumerate(). -libusb HIDAPI programs are encouraged to use the interface number -instead to differentiate between interfaces on a composite HID device. */ -/*#define INVASIVE_GET_USAGE*/ - -/* Linked List of input reports received from the device. */ -struct input_report { - uint8_t *data; - size_t len; - struct input_report *next; +/* USB HID device property names */ +const char *device_string_names[] = { + "manufacturer", + "product", + "serial", }; +/* Symbolic names for the properties above */ +enum device_string_id { + DEVICE_STRING_MANUFACTURER, + DEVICE_STRING_PRODUCT, + DEVICE_STRING_SERIAL, + + DEVICE_STRING_COUNT, +}; struct hid_device_ { - /* Handle to the actual device. */ - libusb_device_handle *device_handle; - - /* Endpoint information */ - int input_endpoint; - int output_endpoint; - int input_ep_max_packet_size; - - /* The interface number of the HID */ - int interface; - - /* Indexes of Strings */ - int manufacturer_index; - int product_index; - int serial_index; - - /* Whether blocking reads are used */ - int blocking; /* boolean */ - - /* Read thread objects */ - pthread_t thread; - pthread_mutex_t mutex; /* Protects input_reports */ - pthread_cond_t condition; - pthread_barrier_t barrier; /* Ensures correct startup sequence */ - int shutdown_thread; - int cancelled; - struct libusb_transfer *transfer; - - /* List of received input reports. */ - struct input_report *input_reports; + int device_handle; + int blocking; + int uses_numbered_reports; }; -static libusb_context *usb_context = NULL; -uint16_t get_usb_code_for_current_locale(void); -static int return_data(hid_device *dev, unsigned char *data, size_t length); +static __u32 kernel_version = 0; + +static __u32 detect_kernel_version(void) +{ + struct utsname name; + int major, minor, release; + int ret; + + uname(&name); + ret = sscanf(name.release, "%d.%d.%d", &major, &minor, &release); + if (ret == 3) { + return KERNEL_VERSION(major, minor, release); + } + + ret = sscanf(name.release, "%d.%d", &major, &minor); + if (ret == 2) { + return KERNEL_VERSION(major, minor, 0); + } + + printf("Couldn't determine kernel version from version string \"%s\"\n", name.release); + return 0; +} static hid_device *new_hid_device(void) { hid_device *dev = calloc(1, sizeof(hid_device)); + dev->device_handle = -1; dev->blocking = 1; - - pthread_mutex_init(&dev->mutex, NULL); - pthread_cond_init(&dev->condition, NULL); - pthread_barrier_init(&dev->barrier, NULL, 2); + dev->uses_numbered_reports = 0; return dev; } -static void free_hid_device(hid_device *dev) -{ - /* Clean up the thread objects */ - pthread_barrier_destroy(&dev->barrier); - pthread_cond_destroy(&dev->condition); - pthread_mutex_destroy(&dev->mutex); - /* Free the device itself */ - free(dev); +/* The caller must free the returned string with free(). */ +static wchar_t *utf8_to_wchar_t(const char *utf8) +{ + wchar_t *ret = NULL; + + if (utf8) { + size_t wlen = mbstowcs(NULL, utf8, 0); + if ((size_t) -1 == wlen) { + return wcsdup(L""); + } + ret = calloc(wlen+1, sizeof(wchar_t)); + mbstowcs(ret, utf8, wlen+1); + ret[wlen] = 0x0000; + } + + return ret; } -#if 0 -/*TODO: Implement this funciton on hidapi/libusb.. */ -static void register_error(hid_device *dev, const char *op) +/* Get an attribute value from a udev_device and return it as a whar_t + string. The returned string must be freed with free() when done.*/ +static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name) { - -} -#endif - -#ifdef INVASIVE_GET_USAGE -/* Get bytes from a HID Report Descriptor. - Only call with a num_bytes of 0, 1, 2, or 4. */ -static uint32_t get_bytes(uint8_t *rpt, size_t len, size_t num_bytes, size_t cur) -{ - /* Return if there aren't enough bytes. */ - if (cur + num_bytes >= len) - return 0; - - if (num_bytes == 0) - return 0; - else if (num_bytes == 1) { - return rpt[cur+1]; - } - else if (num_bytes == 2) { - return (rpt[cur+2] * 256 + rpt[cur+1]); - } - else if (num_bytes == 4) { - return (rpt[cur+4] * 0x01000000 + - rpt[cur+3] * 0x00010000 + - rpt[cur+2] * 0x00000100 + - rpt[cur+1] * 0x00000001); - } - else - return 0; + return utf8_to_wchar_t(udev_device_get_sysattr_value(dev, udev_name)); } -/* Retrieves the device's Usage Page and Usage from the report - descriptor. The algorithm is simple, as it just returns the first - Usage and Usage Page that it finds in the descriptor. - The return value is 0 on success and -1 on failure. */ -static int get_usage(uint8_t *report_descriptor, size_t size, - unsigned short *usage_page, unsigned short *usage) -{ +/* uses_numbered_reports() returns 1 if report_descriptor describes a device + which contains numbered reports. */ +static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) { unsigned int i = 0; int size_code; int data_len, key_size; - int usage_found = 0, usage_page_found = 0; while (i < size) { int key = report_descriptor[i]; - int key_cmd = key & 0xfc; + + /* Check for the Report ID key */ + if (key == 0x85/*Report ID*/) { + /* This device has a Report ID, which means it uses + numbered reports. */ + return 1; + } //printf("key: %02hhx\n", key); @@ -291,390 +191,379 @@ static int get_usage(uint8_t *report_descriptor, size_t size, key_size = 1; } - if (key_cmd == 0x4) { - *usage_page = get_bytes(report_descriptor, size, data_len, i); - usage_page_found = 1; - //printf("Usage Page: %x\n", (uint32_t)*usage_page); - } - if (key_cmd == 0x8) { - *usage = get_bytes(report_descriptor, size, data_len, i); - usage_found = 1; - //printf("Usage: %x\n", (uint32_t)*usage); - } - - if (usage_page_found && usage_found) - return 0; /* success */ - /* Skip over this key and it's associated data */ i += data_len + key_size; } - return -1; /* failure */ -} -#endif /* INVASIVE_GET_USAGE */ - -#if defined(__FreeBSD__) && __FreeBSD__ < 10 -/* The libusb version included in FreeBSD < 10 doesn't have this function. In - mainline libusb, it's inlined in libusb.h. This function will bear a striking - resemblance to that one, because there's about one way to code it. - - Note that the data parameter is Unicode in UTF-16LE encoding. - Return value is the number of bytes in data, or LIBUSB_ERROR_*. - */ -static inline int libusb_get_string_descriptor(libusb_device_handle *dev, - uint8_t descriptor_index, uint16_t lang_id, - unsigned char *data, int length) -{ - return libusb_control_transfer(dev, - LIBUSB_ENDPOINT_IN | 0x0, /* Endpoint 0 IN */ - LIBUSB_REQUEST_GET_DESCRIPTOR, - (LIBUSB_DT_STRING << 8) | descriptor_index, - lang_id, data, (uint16_t) length, 1000); -} - -#endif - - -/* Get the first language the device says it reports. This comes from - USB string #0. */ -static uint16_t get_first_language(libusb_device_handle *dev) -{ - uint16_t buf[32]; - int len; - - /* Get the string from libusb. */ - len = libusb_get_string_descriptor(dev, - 0x0, /* String ID */ - 0x0, /* Language */ - (unsigned char*)buf, - sizeof(buf)); - if (len < 4) - return 0x0; - - return buf[1]; /* First two bytes are len and descriptor type. */ -} - -static int is_language_supported(libusb_device_handle *dev, uint16_t lang) -{ - uint16_t buf[32]; - int len; - int i; - - /* Get the string from libusb. */ - len = libusb_get_string_descriptor(dev, - 0x0, /* String ID */ - 0x0, /* Language */ - (unsigned char*)buf, - sizeof(buf)); - if (len < 4) - return 0x0; - - - len /= 2; /* language IDs are two-bytes each. */ - /* Start at index 1 because there are two bytes of protocol data. */ - for (i = 1; i < len; i++) { - if (buf[i] == lang) - return 1; - } - + /* Didn't find a Report ID key. Device doesn't use numbered reports. */ return 0; } - -/* This function returns a newly allocated wide string containing the USB - device string numbered by the index. The returned string must be freed - by using free(). */ -static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx) +/* + * The caller is responsible for free()ing the (newly-allocated) character + * strings pointed to by serial_number_utf8 and product_name_utf8 after use. + */ +static int +parse_uevent_info(const char *uevent, int *bus_type, + unsigned short *vendor_id, unsigned short *product_id, + char **serial_number_utf8, char **product_name_utf8) { - char buf[512]; - int len; - wchar_t *str = NULL; + char *tmp = strdup(uevent); + char *saveptr = NULL; + char *line; + char *key; + char *value; -#ifndef __ANDROID__ /* we don't use iconv on Android */ - wchar_t wbuf[256]; - /* iconv variables */ - iconv_t ic; - size_t inbytes; - size_t outbytes; - size_t res; -#ifdef __FreeBSD__ - const char *inptr; -#else - char *inptr; -#endif - char *outptr; -#endif + int found_id = 0; + int found_serial = 0; + int found_name = 0; - /* Determine which language to use. */ - uint16_t lang; - lang = get_usb_code_for_current_locale(); - if (!is_language_supported(dev, lang)) - lang = get_first_language(dev); + line = strtok_r(tmp, "\n", &saveptr); + while (line != NULL) { + /* line: "KEY=value" */ + key = line; + value = strchr(line, '='); + if (!value) { + goto next_line; + } + *value = '\0'; + value++; - /* Get the string from libusb. */ - len = libusb_get_string_descriptor(dev, - idx, - lang, - (unsigned char*)buf, - sizeof(buf)); - if (len < 0) - return NULL; + if (strcmp(key, "HID_ID") == 0) { + /** + * type vendor product + * HID_ID=0003:000005AC:00008242 + **/ + int ret = sscanf(value, "%x:%hx:%hx", bus_type, vendor_id, product_id); + if (ret == 3) { + found_id = 1; + } + } else if (strcmp(key, "HID_NAME") == 0) { + /* The caller has to free the product name */ + *product_name_utf8 = strdup(value); + found_name = 1; + } else if (strcmp(key, "HID_UNIQ") == 0) { + /* The caller has to free the serial number */ + *serial_number_utf8 = strdup(value); + found_serial = 1; + } -#ifdef __ANDROID__ - - /* Bionic does not have iconv support nor wcsdup() function, so it - has to be done manually. The following code will only work for - code points that can be represented as a single UTF-16 character, - and will incorrectly convert any code points which require more - than one UTF-16 character. - - Skip over the first character (2-bytes). */ - len -= 2; - str = malloc((len / 2 + 1) * sizeof(wchar_t)); - int i; - for (i = 0; i < len / 2; i++) { - str[i] = buf[i * 2 + 2] | (buf[i * 2 + 3] << 8); - } - str[len / 2] = 0x00000000; - -#else - - /* buf does not need to be explicitly NULL-terminated because - it is only passed into iconv() which does not need it. */ - - /* Initialize iconv. */ - ic = iconv_open("WCHAR_T", "UTF-16LE"); - if (ic == (iconv_t)-1) { - LOG("iconv_open() failed\n"); - return NULL; +next_line: + line = strtok_r(NULL, "\n", &saveptr); } - /* Convert to native wchar_t (UTF-32 on glibc/BSD systems). - Skip the first character (2-bytes). */ - inptr = buf+2; - inbytes = len-2; - outptr = (char*) wbuf; - outbytes = sizeof(wbuf); - res = iconv(ic, &inptr, &inbytes, &outptr, &outbytes); - if (res == (size_t)-1) { - LOG("iconv() failed\n"); - goto err; - } - - /* Write the terminating NULL. */ - wbuf[sizeof(wbuf)/sizeof(wbuf[0])-1] = 0x00000000; - if (outbytes >= sizeof(wbuf[0])) - *((wchar_t*)outptr) = 0x00000000; - - /* Allocate and copy the string. */ - str = wcsdup(wbuf); - -err: - iconv_close(ic); - -#endif - - return str; + free(tmp); + return (found_id && found_name && found_serial); } -static char *make_path(libusb_device *dev, int interface_number) + +static int get_device_string(hid_device *dev, enum device_string_id key, wchar_t *string, size_t maxlen) { - char str[64]; - snprintf(str, sizeof(str), "%04x:%04x:%02x", - libusb_get_bus_number(dev), - libusb_get_device_address(dev), - interface_number); - str[sizeof(str)-1] = '\0'; + struct udev *udev; + struct udev_device *udev_dev, *parent, *hid_dev; + struct stat s; + int ret = -1; + char *serial_number_utf8 = NULL; + char *product_name_utf8 = NULL; - return strdup(str); + /* Create the udev object */ + udev = udev_new(); + if (!udev) { + printf("Can't create udev\n"); + return -1; + } + + /* Get the dev_t (major/minor numbers) from the file handle. */ + ret = fstat(dev->device_handle, &s); + if (-1 == ret) + return ret; + /* Open a udev device from the dev_t. 'c' means character device. */ + udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev); + if (udev_dev) { + hid_dev = udev_device_get_parent_with_subsystem_devtype( + udev_dev, + "hid", + NULL); + if (hid_dev) { + unsigned short dev_vid; + unsigned short dev_pid; + int bus_type; + size_t retm; + + ret = parse_uevent_info( + udev_device_get_sysattr_value(hid_dev, "uevent"), + &bus_type, + &dev_vid, + &dev_pid, + &serial_number_utf8, + &product_name_utf8); + + if (bus_type == BUS_BLUETOOTH) { + switch (key) { + case DEVICE_STRING_MANUFACTURER: + wcsncpy(string, L"", maxlen); + ret = 0; + break; + case DEVICE_STRING_PRODUCT: + retm = mbstowcs(string, product_name_utf8, maxlen); + ret = (retm == (size_t)-1)? -1: 0; + break; + case DEVICE_STRING_SERIAL: + retm = mbstowcs(string, serial_number_utf8, maxlen); + ret = (retm == (size_t)-1)? -1: 0; + break; + case DEVICE_STRING_COUNT: + default: + ret = -1; + break; + } + } + else { + /* This is a USB device. Find its parent USB Device node. */ + parent = udev_device_get_parent_with_subsystem_devtype( + udev_dev, + "usb", + "usb_device"); + if (parent) { + const char *str; + const char *key_str = NULL; + + if (key >= 0 && key < DEVICE_STRING_COUNT) { + key_str = device_string_names[key]; + } else { + ret = -1; + goto end; + } + + str = udev_device_get_sysattr_value(parent, key_str); + if (str) { + /* Convert the string from UTF-8 to wchar_t */ + retm = mbstowcs(string, str, maxlen); + ret = (retm == (size_t)-1)? -1: 0; + goto end; + } + } + } + } + } + +end: + free(serial_number_utf8); + free(product_name_utf8); + + udev_device_unref(udev_dev); + /* parent and hid_dev don't need to be (and can't be) unref'd. + I'm not sure why, but they'll throw double-free() errors. */ + udev_unref(udev); + + return ret; } - int HID_API_EXPORT hid_init(void) { - if (!usb_context) { - const char *locale; + const char *locale; - /* Init Libusb */ - if (libusb_init(&usb_context)) - return -1; + /* Set the locale if it's not set. */ + locale = setlocale(LC_CTYPE, NULL); + if (!locale) + setlocale(LC_CTYPE, ""); - /* Set the locale if it's not set. */ - locale = setlocale(LC_CTYPE, NULL); - if (!locale) - setlocale(LC_CTYPE, ""); - } + kernel_version = detect_kernel_version(); return 0; } int HID_API_EXPORT hid_exit(void) { - if (usb_context) { - libusb_exit(usb_context); - usb_context = NULL; - } - + /* Nothing to do for this in the Linux/hidraw implementation. */ return 0; } + struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) { - libusb_device **devs; - libusb_device *dev; - libusb_device_handle *handle; - ssize_t num_devs; - int i = 0; + struct udev *udev; + struct udev_enumerate *enumerate; + struct udev_list_entry *devices, *dev_list_entry; struct hid_device_info *root = NULL; /* return object */ struct hid_device_info *cur_dev = NULL; + struct hid_device_info *prev_dev = NULL; /* previous device */ - if(hid_init() < 0) + hid_init(); + + /* Create the udev object */ + udev = udev_new(); + if (!udev) { + printf("Can't create udev\n"); return NULL; - - num_devs = libusb_get_device_list(usb_context, &devs); - if (num_devs < 0) - return NULL; - while ((dev = devs[i++]) != NULL) { - struct libusb_device_descriptor desc; - struct libusb_config_descriptor *conf_desc = NULL; - int j, k; - int interface_num = 0; - - int res = libusb_get_device_descriptor(dev, &desc); - unsigned short dev_vid = desc.idVendor; - unsigned short dev_pid = desc.idProduct; - - res = libusb_get_active_config_descriptor(dev, &conf_desc); - if (res < 0) - libusb_get_config_descriptor(dev, 0, &conf_desc); - if (conf_desc) { - for (j = 0; j < conf_desc->bNumInterfaces; j++) { - const struct libusb_interface *intf = &conf_desc->interface[j]; - for (k = 0; k < intf->num_altsetting; k++) { - const struct libusb_interface_descriptor *intf_desc; - intf_desc = &intf->altsetting[k]; - if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { - interface_num = intf_desc->bInterfaceNumber; - - /* Check the VID/PID against the arguments */ - if ((vendor_id == 0x0 || vendor_id == dev_vid) && - (product_id == 0x0 || product_id == dev_pid)) { - struct hid_device_info *tmp; - - /* VID/PID match. Create the record. */ - tmp = calloc(1, sizeof(struct hid_device_info)); - if (cur_dev) { - cur_dev->next = tmp; - } - else { - root = tmp; - } - cur_dev = tmp; - - /* Fill out the record */ - cur_dev->next = NULL; - cur_dev->path = make_path(dev, interface_num); - - res = libusb_open(dev, &handle); - - if (res >= 0) { - /* Serial Number */ - if (desc.iSerialNumber > 0) - cur_dev->serial_number = - get_usb_string(handle, desc.iSerialNumber); - - /* Manufacturer and Product strings */ - if (desc.iManufacturer > 0) - cur_dev->manufacturer_string = - get_usb_string(handle, desc.iManufacturer); - if (desc.iProduct > 0) - cur_dev->product_string = - get_usb_string(handle, desc.iProduct); - -#ifdef INVASIVE_GET_USAGE -{ - /* - This section is removed because it is too - invasive on the system. Getting a Usage Page - and Usage requires parsing the HID Report - descriptor. Getting a HID Report descriptor - involves claiming the interface. Claiming the - interface involves detaching the kernel driver. - Detaching the kernel driver is hard on the system - because it will unclaim interfaces (if another - app has them claimed) and the re-attachment of - the driver will sometimes change /dev entry names. - It is for these reasons that this section is - #if 0. For composite devices, use the interface - field in the hid_device_info struct to distinguish - between interfaces. */ - unsigned char data[256]; -#ifdef DETACH_KERNEL_DRIVER - int detached = 0; - /* Usage Page and Usage */ - res = libusb_kernel_driver_active(handle, interface_num); - if (res == 1) { - res = libusb_detach_kernel_driver(handle, interface_num); - if (res < 0) - LOG("Couldn't detach kernel driver, even though a kernel driver was attached."); - else - detached = 1; - } -#endif - res = libusb_claim_interface(handle, interface_num); - if (res >= 0) { - /* Get the HID Report Descriptor. */ - res = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8)|interface_num, 0, data, sizeof(data), 5000); - if (res >= 0) { - unsigned short page=0, usage=0; - /* Parse the usage and usage page - out of the report descriptor. */ - get_usage(data, res, &page, &usage); - cur_dev->usage_page = page; - cur_dev->usage = usage; - } - else - LOG("libusb_control_transfer() for getting the HID report failed with %d\n", res); - - /* Release the interface */ - res = libusb_release_interface(handle, interface_num); - if (res < 0) - LOG("Can't release the interface.\n"); - } - else - LOG("Can't claim interface %d\n", res); -#ifdef DETACH_KERNEL_DRIVER - /* Re-attach kernel driver if necessary. */ - if (detached) { - res = libusb_attach_kernel_driver(handle, interface_num); - if (res < 0) - LOG("Couldn't re-attach kernel driver.\n"); - } -#endif -} -#endif /* INVASIVE_GET_USAGE */ - - libusb_close(handle); - } - /* VID/PID */ - cur_dev->vendor_id = dev_vid; - cur_dev->product_id = dev_pid; - - /* Release Number */ - cur_dev->release_number = desc.bcdDevice; - - /* Interface Number */ - cur_dev->interface_number = interface_num; - } - } - } /* altsettings */ - } /* interfaces */ - libusb_free_config_descriptor(conf_desc); - } } - libusb_free_device_list(devs, 1); + /* Create a list of the devices in the 'hidraw' subsystem. */ + enumerate = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(enumerate, "hidraw"); + udev_enumerate_scan_devices(enumerate); + devices = udev_enumerate_get_list_entry(enumerate); + /* For each item, see if it matches the vid/pid, and if so + create a udev_device record for it */ + udev_list_entry_foreach(dev_list_entry, devices) { + const char *sysfs_path; + const char *dev_path; + const char *str; + struct udev_device *raw_dev; /* The device's hidraw udev node. */ + struct udev_device *hid_dev; /* The device's HID udev node. */ + struct udev_device *usb_dev; /* The device's USB udev node. */ + struct udev_device *intf_dev; /* The device's interface (in the USB sense). */ + unsigned short dev_vid; + unsigned short dev_pid; + char *serial_number_utf8 = NULL; + char *product_name_utf8 = NULL; + int bus_type; + int result; + + /* Get the filename of the /sys entry for the device + and create a udev_device object (dev) representing it */ + sysfs_path = udev_list_entry_get_name(dev_list_entry); + raw_dev = udev_device_new_from_syspath(udev, sysfs_path); + dev_path = udev_device_get_devnode(raw_dev); + + hid_dev = udev_device_get_parent_with_subsystem_devtype( + raw_dev, + "hid", + NULL); + + if (!hid_dev) { + /* Unable to find parent hid device. */ + goto next; + } + + result = parse_uevent_info( + udev_device_get_sysattr_value(hid_dev, "uevent"), + &bus_type, + &dev_vid, + &dev_pid, + &serial_number_utf8, + &product_name_utf8); + + if (!result) { + /* parse_uevent_info() failed for at least one field. */ + goto next; + } + + if (bus_type != BUS_USB && bus_type != BUS_BLUETOOTH) { + /* We only know how to handle USB and BT devices. */ + goto next; + } + + /* Check the VID/PID against the arguments */ + if ((vendor_id == 0x0 || vendor_id == dev_vid) && + (product_id == 0x0 || product_id == dev_pid)) { + struct hid_device_info *tmp; + + /* VID/PID match. Create the record. */ + tmp = malloc(sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + prev_dev = cur_dev; + cur_dev = tmp; + + /* Fill out the record */ + cur_dev->next = NULL; + cur_dev->path = dev_path? strdup(dev_path): NULL; + + /* VID/PID */ + cur_dev->vendor_id = dev_vid; + cur_dev->product_id = dev_pid; + + /* Serial Number */ + cur_dev->serial_number = utf8_to_wchar_t(serial_number_utf8); + + /* Release Number */ + cur_dev->release_number = 0x0; + + /* Interface Number */ + cur_dev->interface_number = -1; + + switch (bus_type) { + case BUS_USB: + /* The device pointed to by raw_dev contains information about + the hidraw device. In order to get information about the + USB device, get the parent device with the + subsystem/devtype pair of "usb"/"usb_device". This will + be several levels up the tree, but the function will find + it. */ + usb_dev = udev_device_get_parent_with_subsystem_devtype( + raw_dev, + "usb", + "usb_device"); + + if (!usb_dev) { + /* Free this device */ + free(cur_dev->serial_number); + free(cur_dev->path); + free(cur_dev); + + /* Take it off the device list. */ + if (prev_dev) { + prev_dev->next = NULL; + cur_dev = prev_dev; + } + else { + cur_dev = root = NULL; + } + + goto next; + } + + /* Manufacturer and Product strings */ + cur_dev->manufacturer_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_MANUFACTURER]); + cur_dev->product_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_PRODUCT]); + + /* Release Number */ + str = udev_device_get_sysattr_value(usb_dev, "bcdDevice"); + cur_dev->release_number = (str)? strtol(str, NULL, 16): 0x0; + + /* Get a handle to the interface's udev node. */ + intf_dev = udev_device_get_parent_with_subsystem_devtype( + raw_dev, + "usb", + "usb_interface"); + if (intf_dev) { + str = udev_device_get_sysattr_value(intf_dev, "bInterfaceNumber"); + cur_dev->interface_number = (str)? strtol(str, NULL, 16): -1; + } + + break; + + case BUS_BLUETOOTH: + /* Manufacturer and Product strings */ + cur_dev->manufacturer_string = wcsdup(L""); + cur_dev->product_string = utf8_to_wchar_t(product_name_utf8); + + break; + + default: + /* Unknown device type - this should never happen, as we + * check for USB and Bluetooth devices above */ + break; + } + } + + next: + free(serial_number_utf8); + free(product_name_utf8); + udev_device_unref(raw_dev); + /* hid_dev, usb_dev and intf_dev don't need to be (and can't be) + unref()d. It will cause a double-free() error. I'm not + sure why. */ + } + /* Free the enumerator and udev objects. */ + udev_enumerate_unref(enumerate); + udev_unref(udev); return root; } @@ -705,8 +594,7 @@ hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const if (cur_dev->vendor_id == vendor_id && cur_dev->product_id == product_id) { if (serial_number) { - if (cur_dev->serial_number && - wcscmp(serial_number, cur_dev->serial_number) == 0) { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) { path_to_open = cur_dev->path; break; } @@ -729,272 +617,49 @@ hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const return handle; } -static void read_callback(struct libusb_transfer *transfer) -{ - hid_device *dev = transfer->user_data; - int res; - - if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { - - struct input_report *rpt = malloc(sizeof(*rpt)); - rpt->data = malloc(transfer->actual_length); - memcpy(rpt->data, transfer->buffer, transfer->actual_length); - rpt->len = transfer->actual_length; - rpt->next = NULL; - - pthread_mutex_lock(&dev->mutex); - - /* Attach the new report object to the end of the list. */ - if (dev->input_reports == NULL) { - /* The list is empty. Put it at the root. */ - dev->input_reports = rpt; - pthread_cond_signal(&dev->condition); - } - else { - /* Find the end of the list and attach. */ - struct input_report *cur = dev->input_reports; - int num_queued = 0; - while (cur->next != NULL) { - cur = cur->next; - num_queued++; - } - cur->next = rpt; - - /* Pop one off if we've reached 30 in the queue. This - way we don't grow forever if the user never reads - anything from the device. */ - if (num_queued > 30) { - return_data(dev, NULL, 0); - } - } - pthread_mutex_unlock(&dev->mutex); - } - else if (transfer->status == LIBUSB_TRANSFER_CANCELLED) { - dev->shutdown_thread = 1; - dev->cancelled = 1; - return; - } - else if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) { - dev->shutdown_thread = 1; - dev->cancelled = 1; - return; - } - else if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) { - //LOG("Timeout (normal)\n"); - } - else { - LOG("Unknown transfer code: %d\n", transfer->status); - } - - /* Re-submit the transfer object. */ - res = libusb_submit_transfer(transfer); - if (res != 0) { - LOG("Unable to submit URB. libusb error code: %d\n", res); - dev->shutdown_thread = 1; - dev->cancelled = 1; - } -} - - -static void *read_thread(void *param) -{ - hid_device *dev = param; - unsigned char *buf; - const size_t length = dev->input_ep_max_packet_size; - - /* Set up the transfer object. */ - buf = malloc(length); - dev->transfer = libusb_alloc_transfer(0); - libusb_fill_interrupt_transfer(dev->transfer, - dev->device_handle, - dev->input_endpoint, - buf, - length, - read_callback, - dev, - 5000/*timeout*/); - - /* Make the first submission. Further submissions are made - from inside read_callback() */ - libusb_submit_transfer(dev->transfer); - - /* Notify the main thread that the read thread is up and running. */ - pthread_barrier_wait(&dev->barrier); - - /* Handle all the events. */ - while (!dev->shutdown_thread) { - int res; - res = libusb_handle_events(usb_context); - if (res < 0) { - /* There was an error. */ - LOG("read_thread(): libusb reports error # %d\n", res); - - /* Break out of this loop only on fatal error.*/ - if (res != LIBUSB_ERROR_BUSY && - res != LIBUSB_ERROR_TIMEOUT && - res != LIBUSB_ERROR_OVERFLOW && - res != LIBUSB_ERROR_INTERRUPTED) { - break; - } - } - } - - /* Cancel any transfer that may be pending. This call will fail - if no transfers are pending, but that's OK. */ - libusb_cancel_transfer(dev->transfer); - - while (!dev->cancelled) - libusb_handle_events_completed(usb_context, &dev->cancelled); - - /* Now that the read thread is stopping, Wake any threads which are - waiting on data (in hid_read_timeout()). Do this under a mutex to - make sure that a thread which is about to go to sleep waiting on - the condition actually will go to sleep before the condition is - signaled. */ - pthread_mutex_lock(&dev->mutex); - pthread_cond_broadcast(&dev->condition); - pthread_mutex_unlock(&dev->mutex); - - /* The dev->transfer->buffer and dev->transfer objects are cleaned up - in hid_close(). They are not cleaned up here because this thread - could end either due to a disconnect or due to a user - call to hid_close(). In both cases the objects can be safely - cleaned up after the call to pthread_join() (in hid_close()), but - since hid_close() calls libusb_cancel_transfer(), on these objects, - they can not be cleaned up here. */ - - return NULL; -} - - hid_device * HID_API_EXPORT hid_open_path(const char *path) { hid_device *dev = NULL; - libusb_device **devs; - libusb_device *usb_dev; - int res; - int d = 0; - int good_open = 0; - - if(hid_init() < 0) - return NULL; + hid_init(); dev = new_hid_device(); - libusb_get_device_list(usb_context, &devs); - while ((usb_dev = devs[d++]) != NULL) { - struct libusb_device_descriptor desc; - struct libusb_config_descriptor *conf_desc = NULL; - int i,j,k; - libusb_get_device_descriptor(usb_dev, &desc); - - if (libusb_get_active_config_descriptor(usb_dev, &conf_desc) < 0) - continue; - for (j = 0; j < conf_desc->bNumInterfaces; j++) { - const struct libusb_interface *intf = &conf_desc->interface[j]; - for (k = 0; k < intf->num_altsetting; k++) { - const struct libusb_interface_descriptor *intf_desc; - intf_desc = &intf->altsetting[k]; - if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { - char *dev_path = make_path(usb_dev, intf_desc->bInterfaceNumber); - if (!strcmp(dev_path, path)) { - /* Matched Paths. Open this device */ - - /* OPEN HERE */ - res = libusb_open(usb_dev, &dev->device_handle); - if (res < 0) { - LOG("can't open device\n"); - free(dev_path); - break; - } - good_open = 1; -#ifdef DETACH_KERNEL_DRIVER - /* Detach the kernel driver, but only if the - device is managed by the kernel */ - if (libusb_kernel_driver_active(dev->device_handle, intf_desc->bInterfaceNumber) == 1) { - res = libusb_detach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber); - if (res < 0) { - libusb_close(dev->device_handle); - LOG("Unable to detach Kernel Driver\n"); - free(dev_path); - good_open = 0; - break; - } - } -#endif - res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber); - if (res < 0) { - LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res); - free(dev_path); - libusb_close(dev->device_handle); - good_open = 0; - break; - } - - /* Store off the string descriptor indexes */ - dev->manufacturer_index = desc.iManufacturer; - dev->product_index = desc.iProduct; - dev->serial_index = desc.iSerialNumber; - - /* Store off the interface number */ - dev->interface = intf_desc->bInterfaceNumber; - - /* Find the INPUT and OUTPUT endpoints. An - OUTPUT endpoint is not required. */ - for (i = 0; i < intf_desc->bNumEndpoints; i++) { - const struct libusb_endpoint_descriptor *ep - = &intf_desc->endpoint[i]; - - /* Determine the type and direction of this - endpoint. */ - int is_interrupt = - (ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) - == LIBUSB_TRANSFER_TYPE_INTERRUPT; - int is_output = - (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) - == LIBUSB_ENDPOINT_OUT; - int is_input = - (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) - == LIBUSB_ENDPOINT_IN; - - /* Decide whether to use it for input or output. */ - if (dev->input_endpoint == 0 && - is_interrupt && is_input) { - /* Use this endpoint for INPUT */ - dev->input_endpoint = ep->bEndpointAddress; - dev->input_ep_max_packet_size = ep->wMaxPacketSize; - } - if (dev->output_endpoint == 0 && - is_interrupt && is_output) { - /* Use this endpoint for OUTPUT */ - dev->output_endpoint = ep->bEndpointAddress; - } - } - - pthread_create(&dev->thread, NULL, read_thread, dev); - - /* Wait here for the read thread to be initialized. */ - pthread_barrier_wait(&dev->barrier); - - } - free(dev_path); - } - } - } - libusb_free_config_descriptor(conf_desc); - - } - - libusb_free_device_list(devs, 1); + /* OPEN HERE */ + dev->device_handle = open(path, O_RDWR); /* If we have a good handle, return it. */ - if (good_open) { + if (dev->device_handle > 0) { + + /* Get the report descriptor */ + int res, desc_size = 0; + struct hidraw_report_descriptor rpt_desc; + + memset(&rpt_desc, 0x0, sizeof(rpt_desc)); + + /* Get Report Descriptor Size */ + res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size); + if (res < 0) + perror("HIDIOCGRDESCSIZE"); + + + /* Get Report Descriptor */ + rpt_desc.size = desc_size; + res = ioctl(dev->device_handle, HIDIOCGRDESC, &rpt_desc); + if (res < 0) { + perror("HIDIOCGRDESC"); + } else { + /* Determine if this device uses numbered reports. */ + dev->uses_numbered_reports = + uses_numbered_reports(rpt_desc.value, + rpt_desc.size); + } + return dev; } else { /* Unable to open any devices. */ - free_hid_device(dev); + free(dev); return NULL; } } @@ -1002,231 +667,95 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) { - int res; - int report_number = data[0]; - int skipped_report_id = 0; + int bytes_written; - if (report_number == 0x0) { - data++; - length--; - skipped_report_id = 1; - } + bytes_written = write(dev->device_handle, data, length); - - if (dev->output_endpoint <= 0) { - /* No interrupt out endpoint. Use the Control Endpoint */ - res = libusb_control_transfer(dev->device_handle, - LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT, - 0x09/*HID Set_Report*/, - (2/*HID output*/ << 8) | report_number, - dev->interface, - (unsigned char *)data, length, - 1000/*timeout millis*/); - - if (res < 0) - return -1; - - if (skipped_report_id) - length++; - - return length; - } - else { - /* Use the interrupt out endpoint */ - int actual_length; - res = libusb_interrupt_transfer(dev->device_handle, - dev->output_endpoint, - (unsigned char*)data, - length, - &actual_length, 1000); - - if (res < 0) - return -1; - - if (skipped_report_id) - actual_length++; - - return actual_length; - } -} - -/* Helper function, to simplify hid_read(). - This should be called with dev->mutex locked. */ -static int return_data(hid_device *dev, unsigned char *data, size_t length) -{ - /* Copy the data out of the linked list item (rpt) into the - return buffer (data), and delete the liked list item. */ - struct input_report *rpt = dev->input_reports; - size_t len = (length < rpt->len)? length: rpt->len; - if (len > 0) - memcpy(data, rpt->data, len); - dev->input_reports = rpt->next; - free(rpt->data); - free(rpt); - return len; -} - -static void cleanup_mutex(void *param) -{ - hid_device *dev = param; - pthread_mutex_unlock(&dev->mutex); + return bytes_written; } int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) { - int bytes_read = -1; + int bytes_read; -#if 0 - int transferred; - int res = libusb_interrupt_transfer(dev->device_handle, dev->input_endpoint, data, length, &transferred, 5000); - LOG("transferred: %d\n", transferred); - return transferred; -#endif + if (milliseconds >= 0) { + /* Milliseconds is either 0 (non-blocking) or > 0 (contains + a valid timeout). In both cases we want to call poll() + and wait for data to arrive. Don't rely on non-blocking + operation (O_NONBLOCK) since some kernels don't seem to + properly report device disconnection through read() when + in non-blocking mode. */ + int ret; + struct pollfd fds; - pthread_mutex_lock(&dev->mutex); - pthread_cleanup_push(&cleanup_mutex, dev); - - /* There's an input report queued up. Return it. */ - if (dev->input_reports) { - /* Return the first one */ - bytes_read = return_data(dev, data, length); - goto ret; - } - - if (dev->shutdown_thread) { - /* This means the device has been disconnected. - An error code of -1 should be returned. */ - bytes_read = -1; - goto ret; - } - - if (milliseconds == -1) { - /* Blocking */ - while (!dev->input_reports && !dev->shutdown_thread) { - pthread_cond_wait(&dev->condition, &dev->mutex); + fds.fd = dev->device_handle; + fds.events = POLLIN; + fds.revents = 0; + ret = poll(&fds, 1, milliseconds); + if (ret == -1 || ret == 0) { + /* Error or timeout */ + return ret; } - if (dev->input_reports) { - bytes_read = return_data(dev, data, length); + else { + /* Check for errors on the file descriptor. This will + indicate a device disconnection. */ + if (fds.revents & (POLLERR | POLLHUP | POLLNVAL)) + return -1; } } - else if (milliseconds > 0) { - /* Non-blocking, but called with timeout. */ - int res; - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - ts.tv_sec += milliseconds / 1000; - ts.tv_nsec += (milliseconds % 1000) * 1000000; - if (ts.tv_nsec >= 1000000000L) { - ts.tv_sec++; - ts.tv_nsec -= 1000000000L; - } - while (!dev->input_reports && !dev->shutdown_thread) { - res = pthread_cond_timedwait(&dev->condition, &dev->mutex, &ts); - if (res == 0) { - if (dev->input_reports) { - bytes_read = return_data(dev, data, length); - break; - } - - /* If we're here, there was a spurious wake up - or the read thread was shutdown. Run the - loop again (ie: don't break). */ - } - else if (res == ETIMEDOUT) { - /* Timed out. */ - bytes_read = 0; - break; - } - else { - /* Error. */ - bytes_read = -1; - break; - } - } - } - else { - /* Purely non-blocking */ + bytes_read = read(dev->device_handle, data, length); + if (bytes_read < 0 && (errno == EAGAIN || errno == EINPROGRESS)) bytes_read = 0; - } -ret: - pthread_mutex_unlock(&dev->mutex); - pthread_cleanup_pop(0); + if (bytes_read >= 0 && + kernel_version != 0 && + kernel_version < KERNEL_VERSION(2,6,34) && + dev->uses_numbered_reports) { + /* Work around a kernel bug. Chop off the first byte. */ + memmove(data, data+1, bytes_read); + bytes_read--; + } return bytes_read; } int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) { - return hid_read_timeout(dev, data, length, dev->blocking ? -1 : 0); + return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); } int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) { - dev->blocking = !nonblock; + /* Do all non-blocking in userspace using poll(), since it looks + like there's a bug in the kernel in some versions where + read() will not return -1 on disconnection of the USB device */ - return 0; + dev->blocking = !nonblock; + return 0; /* Success */ } int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) { - int res = -1; - int skipped_report_id = 0; - int report_number = data[0]; - - if (report_number == 0x0) { - data++; - length--; - skipped_report_id = 1; - } - - res = libusb_control_transfer(dev->device_handle, - LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT, - 0x09/*HID set_report*/, - (3/*HID feature*/ << 8) | report_number, - dev->interface, - (unsigned char *)data, length, - 1000/*timeout millis*/); + int res; + res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data); if (res < 0) - return -1; + perror("ioctl (SFEATURE)"); - /* Account for the report ID */ - if (skipped_report_id) - length++; - - return length; + return res; } int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) { - int res = -1; - int skipped_report_id = 0; - int report_number = data[0]; - - if (report_number == 0x0) { - /* Offset the return buffer by 1, so that the report ID - will remain in byte 0. */ - data++; - length--; - skipped_report_id = 1; - } - res = libusb_control_transfer(dev->device_handle, - LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_IN, - 0x01/*HID get_report*/, - (3/*HID feature*/ << 8) | report_number, - dev->interface, - (unsigned char *)data, length, - 1000/*timeout millis*/); + int res; + res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data); if (res < 0) - return -1; + perror("ioctl (GFEATURE)"); - if (skipped_report_id) - res++; return res; } @@ -1236,63 +765,29 @@ void HID_API_EXPORT hid_close(hid_device *dev) { if (!dev) return; - - /* Cause read_thread() to stop. */ - dev->shutdown_thread = 1; - libusb_cancel_transfer(dev->transfer); - - /* Wait for read_thread() to end. */ - pthread_join(dev->thread, NULL); - - /* Clean up the Transfer objects allocated in read_thread(). */ - free(dev->transfer->buffer); - libusb_free_transfer(dev->transfer); - - /* release the interface */ - libusb_release_interface(dev->device_handle, dev->interface); - - /* Close the handle */ - libusb_close(dev->device_handle); - - /* Clear out the queue of received reports. */ - pthread_mutex_lock(&dev->mutex); - while (dev->input_reports) { - return_data(dev, NULL, 0); - } - pthread_mutex_unlock(&dev->mutex); - - free_hid_device(dev); + close(dev->device_handle); + free(dev); } int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) { - return hid_get_indexed_string(dev, dev->manufacturer_index, string, maxlen); + return get_device_string(dev, DEVICE_STRING_MANUFACTURER, string, maxlen); } int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) { - return hid_get_indexed_string(dev, dev->product_index, string, maxlen); + return get_device_string(dev, DEVICE_STRING_PRODUCT, string, maxlen); } int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) { - return hid_get_indexed_string(dev, dev->serial_index, string, maxlen); + return get_device_string(dev, DEVICE_STRING_SERIAL, string, maxlen); } int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) { - wchar_t *str; - - str = get_usb_string(dev->device_handle, string_index); - if (str) { - wcsncpy(string, str, maxlen); - string[maxlen-1] = L'\0'; - free(str); - return 0; - } - else - return -1; + return -1; } @@ -1300,215 +795,3 @@ HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) { return NULL; } - - -struct lang_map_entry { - const char *name; - const char *string_code; - uint16_t usb_code; -}; - -#define LANG(name,code,usb_code) { name, code, usb_code } -static struct lang_map_entry lang_map[] = { - LANG("Afrikaans", "af", 0x0436), - LANG("Albanian", "sq", 0x041C), - LANG("Arabic - United Arab Emirates", "ar_ae", 0x3801), - LANG("Arabic - Bahrain", "ar_bh", 0x3C01), - LANG("Arabic - Algeria", "ar_dz", 0x1401), - LANG("Arabic - Egypt", "ar_eg", 0x0C01), - LANG("Arabic - Iraq", "ar_iq", 0x0801), - LANG("Arabic - Jordan", "ar_jo", 0x2C01), - LANG("Arabic - Kuwait", "ar_kw", 0x3401), - LANG("Arabic - Lebanon", "ar_lb", 0x3001), - LANG("Arabic - Libya", "ar_ly", 0x1001), - LANG("Arabic - Morocco", "ar_ma", 0x1801), - LANG("Arabic - Oman", "ar_om", 0x2001), - LANG("Arabic - Qatar", "ar_qa", 0x4001), - LANG("Arabic - Saudi Arabia", "ar_sa", 0x0401), - LANG("Arabic - Syria", "ar_sy", 0x2801), - LANG("Arabic - Tunisia", "ar_tn", 0x1C01), - LANG("Arabic - Yemen", "ar_ye", 0x2401), - LANG("Armenian", "hy", 0x042B), - LANG("Azeri - Latin", "az_az", 0x042C), - LANG("Azeri - Cyrillic", "az_az", 0x082C), - LANG("Basque", "eu", 0x042D), - LANG("Belarusian", "be", 0x0423), - LANG("Bulgarian", "bg", 0x0402), - LANG("Catalan", "ca", 0x0403), - LANG("Chinese - China", "zh_cn", 0x0804), - LANG("Chinese - Hong Kong SAR", "zh_hk", 0x0C04), - LANG("Chinese - Macau SAR", "zh_mo", 0x1404), - LANG("Chinese - Singapore", "zh_sg", 0x1004), - LANG("Chinese - Taiwan", "zh_tw", 0x0404), - LANG("Croatian", "hr", 0x041A), - LANG("Czech", "cs", 0x0405), - LANG("Danish", "da", 0x0406), - LANG("Dutch - Netherlands", "nl_nl", 0x0413), - LANG("Dutch - Belgium", "nl_be", 0x0813), - LANG("English - Australia", "en_au", 0x0C09), - LANG("English - Belize", "en_bz", 0x2809), - LANG("English - Canada", "en_ca", 0x1009), - LANG("English - Caribbean", "en_cb", 0x2409), - LANG("English - Ireland", "en_ie", 0x1809), - LANG("English - Jamaica", "en_jm", 0x2009), - LANG("English - New Zealand", "en_nz", 0x1409), - LANG("English - Phillippines", "en_ph", 0x3409), - LANG("English - Southern Africa", "en_za", 0x1C09), - LANG("English - Trinidad", "en_tt", 0x2C09), - LANG("English - Great Britain", "en_gb", 0x0809), - LANG("English - United States", "en_us", 0x0409), - LANG("Estonian", "et", 0x0425), - LANG("Farsi", "fa", 0x0429), - LANG("Finnish", "fi", 0x040B), - LANG("Faroese", "fo", 0x0438), - LANG("French - France", "fr_fr", 0x040C), - LANG("French - Belgium", "fr_be", 0x080C), - LANG("French - Canada", "fr_ca", 0x0C0C), - LANG("French - Luxembourg", "fr_lu", 0x140C), - LANG("French - Switzerland", "fr_ch", 0x100C), - LANG("Gaelic - Ireland", "gd_ie", 0x083C), - LANG("Gaelic - Scotland", "gd", 0x043C), - LANG("German - Germany", "de_de", 0x0407), - LANG("German - Austria", "de_at", 0x0C07), - LANG("German - Liechtenstein", "de_li", 0x1407), - LANG("German - Luxembourg", "de_lu", 0x1007), - LANG("German - Switzerland", "de_ch", 0x0807), - LANG("Greek", "el", 0x0408), - LANG("Hebrew", "he", 0x040D), - LANG("Hindi", "hi", 0x0439), - LANG("Hungarian", "hu", 0x040E), - LANG("Icelandic", "is", 0x040F), - LANG("Indonesian", "id", 0x0421), - LANG("Italian - Italy", "it_it", 0x0410), - LANG("Italian - Switzerland", "it_ch", 0x0810), - LANG("Japanese", "ja", 0x0411), - LANG("Korean", "ko", 0x0412), - LANG("Latvian", "lv", 0x0426), - LANG("Lithuanian", "lt", 0x0427), - LANG("F.Y.R.O. Macedonia", "mk", 0x042F), - LANG("Malay - Malaysia", "ms_my", 0x043E), - LANG("Malay – Brunei", "ms_bn", 0x083E), - LANG("Maltese", "mt", 0x043A), - LANG("Marathi", "mr", 0x044E), - LANG("Norwegian - Bokml", "no_no", 0x0414), - LANG("Norwegian - Nynorsk", "no_no", 0x0814), - LANG("Polish", "pl", 0x0415), - LANG("Portuguese - Portugal", "pt_pt", 0x0816), - LANG("Portuguese - Brazil", "pt_br", 0x0416), - LANG("Raeto-Romance", "rm", 0x0417), - LANG("Romanian - Romania", "ro", 0x0418), - LANG("Romanian - Republic of Moldova", "ro_mo", 0x0818), - LANG("Russian", "ru", 0x0419), - LANG("Russian - Republic of Moldova", "ru_mo", 0x0819), - LANG("Sanskrit", "sa", 0x044F), - LANG("Serbian - Cyrillic", "sr_sp", 0x0C1A), - LANG("Serbian - Latin", "sr_sp", 0x081A), - LANG("Setsuana", "tn", 0x0432), - LANG("Slovenian", "sl", 0x0424), - LANG("Slovak", "sk", 0x041B), - LANG("Sorbian", "sb", 0x042E), - LANG("Spanish - Spain (Traditional)", "es_es", 0x040A), - LANG("Spanish - Argentina", "es_ar", 0x2C0A), - LANG("Spanish - Bolivia", "es_bo", 0x400A), - LANG("Spanish - Chile", "es_cl", 0x340A), - LANG("Spanish - Colombia", "es_co", 0x240A), - LANG("Spanish - Costa Rica", "es_cr", 0x140A), - LANG("Spanish - Dominican Republic", "es_do", 0x1C0A), - LANG("Spanish - Ecuador", "es_ec", 0x300A), - LANG("Spanish - Guatemala", "es_gt", 0x100A), - LANG("Spanish - Honduras", "es_hn", 0x480A), - LANG("Spanish - Mexico", "es_mx", 0x080A), - LANG("Spanish - Nicaragua", "es_ni", 0x4C0A), - LANG("Spanish - Panama", "es_pa", 0x180A), - LANG("Spanish - Peru", "es_pe", 0x280A), - LANG("Spanish - Puerto Rico", "es_pr", 0x500A), - LANG("Spanish - Paraguay", "es_py", 0x3C0A), - LANG("Spanish - El Salvador", "es_sv", 0x440A), - LANG("Spanish - Uruguay", "es_uy", 0x380A), - LANG("Spanish - Venezuela", "es_ve", 0x200A), - LANG("Southern Sotho", "st", 0x0430), - LANG("Swahili", "sw", 0x0441), - LANG("Swedish - Sweden", "sv_se", 0x041D), - LANG("Swedish - Finland", "sv_fi", 0x081D), - LANG("Tamil", "ta", 0x0449), - LANG("Tatar", "tt", 0X0444), - LANG("Thai", "th", 0x041E), - LANG("Turkish", "tr", 0x041F), - LANG("Tsonga", "ts", 0x0431), - LANG("Ukrainian", "uk", 0x0422), - LANG("Urdu", "ur", 0x0420), - LANG("Uzbek - Cyrillic", "uz_uz", 0x0843), - LANG("Uzbek – Latin", "uz_uz", 0x0443), - LANG("Vietnamese", "vi", 0x042A), - LANG("Xhosa", "xh", 0x0434), - LANG("Yiddish", "yi", 0x043D), - LANG("Zulu", "zu", 0x0435), - LANG(NULL, NULL, 0x0), -}; - -uint16_t get_usb_code_for_current_locale(void) -{ - char *locale; - char search_string[64]; - char *ptr; - struct lang_map_entry *lang; - - /* Get the current locale. */ - locale = setlocale(0, NULL); - if (!locale) - return 0x0; - - /* Make a copy of the current locale string. */ - strncpy(search_string, locale, sizeof(search_string)); - search_string[sizeof(search_string)-1] = '\0'; - - /* Chop off the encoding part, and make it lower case. */ - ptr = search_string; - while (*ptr) { - *ptr = tolower(*ptr); - if (*ptr == '.') { - *ptr = '\0'; - break; - } - ptr++; - } - - /* Find the entry which matches the string code of our locale. */ - lang = lang_map; - while (lang->string_code) { - if (!strcmp(lang->string_code, search_string)) { - return lang->usb_code; - } - lang++; - } - - /* There was no match. Find with just the language only. */ - /* Chop off the variant. Chop it off at the '_'. */ - ptr = search_string; - while (*ptr) { - *ptr = tolower(*ptr); - if (*ptr == '_') { - *ptr = '\0'; - break; - } - ptr++; - } - -#if 0 /* TODO: Do we need this? */ - /* Find the entry which matches the string code of our language. */ - lang = lang_map; - while (lang->string_code) { - if (!strcmp(lang->string_code, search_string)) { - return lang->usb_code; - } - lang++; - } -#endif - - /* Found nothing. */ - return 0x0; -} - -#ifdef __cplusplus -} -#endif diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 6218f3837..098dca241 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -169,7 +169,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) encoding_check(libslic3r_gui) -target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES} ${HIDAPI_LIBRARIES}) +target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES} hidapi) if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE) endif () diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 43faf852b..f00f03c13 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -9,6 +9,8 @@ #include +// WARN: If updating these lists, please also update resources/udev/90-3dconnexion.rules + static const std::vector _3DCONNEXION_VENDORS = { 0x046d, // LOGITECH = 1133 // Logitech (3Dconnexion is made by Logitech) From 520a51c62fc958716b867a9138312f8f466cd488 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Mon, 30 Sep 2019 15:20:23 +0200 Subject: [PATCH 05/73] hidapi: Fix include path --- src/CMakeLists.txt | 3 ++- src/slic3r/GUI/Mouse3DController.hpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dc1d5e38c..c087e57e2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,7 +14,6 @@ add_subdirectory(qhull) add_subdirectory(Shiny) add_subdirectory(semver) add_subdirectory(libigl) -add_subdirectory(hidapi) # Adding libnest2d project for bin packing... set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d") @@ -24,6 +23,8 @@ add_subdirectory(libslic3r) if (SLIC3R_GUI) add_subdirectory(imgui) + add_subdirectory(hidapi) + include_directories(hidapi/include) if(WIN32) message(STATUS "WXWIN environment set to: $ENV{WXWIN}") diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index c4b3f78e4..9c7f1ce5e 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -4,7 +4,7 @@ #if ENABLE_3DCONNEXION_DEVICES #include "libslic3r/Point.hpp" -#include "hidapi/hidapi.h" +#include "hidapi.h" #include #include From d58dedd4596e580856ef0f5009dc37fb83583746 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 30 Sep 2019 15:58:45 +0200 Subject: [PATCH 06/73] ENABLE_3DCONNEXION_DEVICES -> Increased limit of device buttons --- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- src/slic3r/GUI/Mouse3DController.cpp | 37 ++++++++++++++++------------ src/slic3r/GUI/Mouse3DController.hpp | 10 +++++++- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 4588084fc..ea7992783 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2551,7 +2551,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) #if ENABLE_3DCONNEXION_DEVICES // try to filter out events coming from mouse 3d controller const Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller(); - if (controller.has_rotation() || controller.has_translation()) + if (controller.has_translation_or_rotation()) return; #endif // ENABLE_3DCONNEXION_DEVICES diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index f00f03c13..10022915a 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -30,7 +30,7 @@ static const std::vector _3DCONNEXION_DEVICES = 0xc629 // SPACEPILOTPRO = 50729 }; -static const unsigned int _3DCONNEXION_BUTTONS_COUNT = 2; +static const unsigned int _3DCONNEXION_MAX_BUTTONS_COUNT = 256; namespace Slic3r { namespace GUI { @@ -41,7 +41,6 @@ const float Mouse3DController::State::DefaultRotationScale = 1.0; Mouse3DController::State::State() : m_translation(Vec3d::Zero()) , m_rotation(Vec3f::Zero()) - , m_buttons(_3DCONNEXION_BUTTONS_COUNT, false) , m_translation_scale(DefaultTranslationScale) , m_rotation_scale(DefaultRotationScale) { @@ -62,8 +61,7 @@ void Mouse3DController::State::set_rotation(const Vec3f& rotation) void Mouse3DController::State::set_button(unsigned int id) { std::lock_guard lock(m_mutex); - if (id < _3DCONNEXION_BUTTONS_COUNT) - m_buttons[id] = true; + m_buttons.push_back(id); } bool Mouse3DController::State::has_translation() const @@ -71,21 +69,23 @@ bool Mouse3DController::State::has_translation() const std::lock_guard lock(m_mutex); return !m_translation.isApprox(Vec3d::Zero()); } + bool Mouse3DController::State::has_rotation() const { std::lock_guard lock(m_mutex); return !m_rotation.isApprox(Vec3f::Zero()); } +bool Mouse3DController::State::has_translation_or_rotation() const +{ + std::lock_guard lock(m_mutex); + return !m_translation.isApprox(Vec3d::Zero()) && !m_rotation.isApprox(Vec3f::Zero()); +} + bool Mouse3DController::State::has_any_button() const { std::lock_guard lock(m_mutex); - for (int i = 0; i < _3DCONNEXION_BUTTONS_COUNT; ++i) - { - if (m_buttons[i]) - return true; - } - return false; + return !m_buttons.empty(); } bool Mouse3DController::State::apply(GLCanvas3D& canvas) @@ -116,12 +116,17 @@ bool Mouse3DController::State::apply(GLCanvas3D& canvas) if (has_any_button()) { - if (m_buttons[0]) - canvas.set_camera_zoom(1.0); - else if (m_buttons[1]) - canvas.set_camera_zoom(-1.0); + for (unsigned int i : m_buttons) + { + switch (i) + { + case 0: { canvas.set_camera_zoom(1.0); break; } + case 1: { canvas.set_camera_zoom(-1.0); break; } + default: { break; } + } + } - m_buttons = std::vector(_3DCONNEXION_BUTTONS_COUNT, false); + m_buttons.clear(); ret = true; } @@ -314,7 +319,7 @@ void Mouse3DController::collect_input() } case Button: { - for (unsigned int i = 0; i < _3DCONNEXION_BUTTONS_COUNT; ++i) + for (unsigned int i = 0; i < _3DCONNEXION_MAX_BUTTONS_COUNT; ++i) { if (retrieved_data[1] & (0x1 << i)) m_state.set_button(i); diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 9c7f1ce5e..64fa34a44 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -26,7 +26,7 @@ class Mouse3DController Vec3d m_translation; Vec3f m_rotation; - std::vector m_buttons; + std::vector m_buttons; double m_translation_scale; float m_rotation_scale; @@ -40,8 +40,15 @@ class Mouse3DController bool has_translation() const; bool has_rotation() const; + bool has_translation_or_rotation() const; bool has_any_button() const; + double get_translation_scale() const { return m_translation_scale; } + void set_translation_scale(double scale) { m_translation_scale = scale; } + + float get_rotation_scale() const { return m_rotation_scale; } + void set_rotation_scale(float scale) { m_rotation_scale = scale; } + // return true if any change to the camera took place bool apply(GLCanvas3D& canvas); }; @@ -71,6 +78,7 @@ public: bool has_translation() const { return m_state.has_translation(); } bool has_rotation() const { return m_state.has_rotation(); } + bool has_translation_or_rotation() const { return m_state.has_translation_or_rotation(); } bool has_any_button() const { return m_state.has_any_button(); } bool apply() From 36d3f90fad0f79e3fefbadacd3394d7d49687ad1 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 2 Oct 2019 15:55:26 +0200 Subject: [PATCH 07/73] ENABLE_3DCONNEXION_DEVICES - Added imgui dialog to edit 3Dconnexion device parameters --- src/slic3r/GUI/GLCanvas3D.cpp | 19 ++++++ src/slic3r/GUI/ImGuiWrapper.cpp | 5 ++ src/slic3r/GUI/KBShortcutsDialog.cpp | 3 + src/slic3r/GUI/Mouse3DController.cpp | 91 +++++++++++++++++++++++++--- src/slic3r/GUI/Mouse3DController.hpp | 16 ++++- 5 files changed, 125 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 2944ffccd..562a939fb 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1636,6 +1636,10 @@ void GLCanvas3D::render() m_camera.debug_render(); #endif // ENABLE_CAMERA_STATISTICS +#if ENABLE_3DCONNEXION_DEVICES + wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog(); +#endif // ENABLE_3DCONNEXION_DEVICES + wxGetApp().imgui()->render(); m_canvas->SwapBuffers(); @@ -2374,6 +2378,21 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) #endif /* __APPLE__ */ post_event(SimpleEvent(EVT_GLTOOLBAR_COPY)); break; + +#if ENABLE_3DCONNEXION_DEVICES +#ifdef __APPLE__ + case 'm': + case 'M': +#else /* __APPLE__ */ + case WXK_CONTROL_M: +#endif /* __APPLE__ */ + { + Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller(); + controller.show_settings_dialog(!controller.is_settings_dialog_shown()); + break; + } +#endif // ENABLE_3DCONNEXION_DEVICES + #ifdef __APPLE__ case 'v': case 'V': diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 8e4d9eebf..7d7e82685 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -528,6 +528,11 @@ void ImGuiWrapper::init_style() // Slider set_color(ImGuiCol_SliderGrab, COL_ORANGE_DARK); set_color(ImGuiCol_SliderGrabActive, COL_ORANGE_LIGHT); + +#if ENABLE_3DCONNEXION_DEVICES + // Separator + set_color(ImGuiCol_Separator, COL_ORANGE_LIGHT); +#endif // ENABLE_3DCONNEXION_DEVICES } void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index e3ef335e6..4bd56ee76 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -157,6 +157,9 @@ void KBShortcutsDialog::fill_shortcuts() plater_shortcuts.push_back(Shortcut("Z", L("Zoom to selected object"))); plater_shortcuts.push_back(Shortcut("I", L("Zoom in"))); plater_shortcuts.push_back(Shortcut("O", L("Zoom out"))); +#if ENABLE_3DCONNEXION_DEVICES + plater_shortcuts.push_back(Shortcut(ctrl+"M", L("Show/Hide 3Dconnexion devices settings dialog"))); +#endif // ENABLE_3DCONNEXION_DEVICES plater_shortcuts.push_back(Shortcut("ESC", L("Unselect gizmo / Clear selection"))); #if ENABLE_RENDER_PICKING_PASS // Don't localize debugging texts. diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 10022915a..18c17f7c2 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -9,6 +9,10 @@ #include +#include + +#include "I18N.hpp" + // WARN: If updating these lists, please also update resources/udev/90-3dconnexion.rules static const std::vector _3DCONNEXION_VENDORS = @@ -64,6 +68,30 @@ void Mouse3DController::State::set_button(unsigned int id) m_buttons.push_back(id); } +void Mouse3DController::State::reset_buttons() +{ + std::lock_guard lock(m_mutex); + return m_buttons.clear(); +} + +const Vec3d& Mouse3DController::State::get_translation() const +{ + std::lock_guard lock(m_mutex); + return m_translation; +} + +const Vec3f& Mouse3DController::State::get_rotation() const +{ + std::lock_guard lock(m_mutex); + return m_rotation; +} + +const std::vector& Mouse3DController::State::get_buttons() const +{ + std::lock_guard lock(m_mutex); + return m_buttons; +} + bool Mouse3DController::State::has_translation() const { std::lock_guard lock(m_mutex); @@ -98,25 +126,28 @@ bool Mouse3DController::State::apply(GLCanvas3D& canvas) if (has_translation()) { - camera.set_target(camera.get_target() + m_translation_scale * (m_translation(0) * camera.get_dir_right() + m_translation(1) * camera.get_dir_forward() + m_translation(2) * camera.get_dir_up())); - m_translation = Vec3d::Zero(); + Vec3d translation = get_translation(); + camera.set_target(camera.get_target() + m_translation_scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up())); + set_translation(Vec3d::Zero()); ret = true; } if (has_rotation()) { - float theta = m_rotation_scale * m_rotation(0); - float phi = m_rotation_scale * m_rotation(2); + Vec3f rotation = get_rotation(); + float theta = m_rotation_scale * rotation(0); + float phi = m_rotation_scale * rotation(2); float sign = camera.inverted_phi ? -1.0f : 1.0f; camera.phi += sign * phi; camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); - m_rotation = Vec3f::Zero(); + set_rotation(Vec3f::Zero()); ret = true; } if (has_any_button()) { - for (unsigned int i : m_buttons) + std::vector buttons = get_buttons(); + for (unsigned int i : buttons) { switch (i) { @@ -126,7 +157,7 @@ bool Mouse3DController::State::apply(GLCanvas3D& canvas) } } - m_buttons.clear(); + reset_buttons(); ret = true; } @@ -138,6 +169,7 @@ Mouse3DController::Mouse3DController() , m_canvas(nullptr) , m_device(nullptr) , m_running(false) + , m_settings_dialog(false) { } @@ -174,6 +206,51 @@ void Mouse3DController::shutdown() m_initialized = false; } +void Mouse3DController::render_settings_dialog() const +{ + if ((m_canvas == nullptr) || !m_running || !m_settings_dialog) + return; + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + + std::lock_guard lock(m_mutex); + Size cnv_size = m_canvas->get_canvas_size(); + + imgui.set_next_window_pos(0.5f * (float)cnv_size.get_width(), 0.5f * (float)cnv_size.get_height(), ImGuiCond_Always, 0.5f, 0.5f); + imgui.set_next_window_bg_alpha(0.5f); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + + imgui.begin(_(L("3Dconnexion settings")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); + + std::vector product(1024, 0); + hid_get_product_string(m_device, product.data(), 1024); + + const ImVec4& color = ImGui::GetStyleColorVec4(ImGuiCol_Separator); + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text(_(L("Device:"))); + ImGui::PopStyleColor(); + ImGui::SameLine(); + imgui.text(boost::nowide::narrow(product.data())); + ImGui::Separator(); + + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text(_(L("Speed:"))); + ImGui::PopStyleColor(); + + float translation = (float)m_state.get_translation_scale() / State::DefaultTranslationScale; + if (ImGui::SliderFloat(_(L("Translation")), &translation, 0.5f, 2.0f, "%.1f")) + m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation); + + float rotation = (float)m_state.get_rotation_scale() / State::DefaultRotationScale; + if (ImGui::SliderFloat(_(L("Rotation")), &rotation, 0.5f, 2.0f, "%.1f")) + m_state.set_rotation_scale(State::DefaultRotationScale * rotation); + + imgui.end(); + + ImGui::PopStyleVar(); +} + void Mouse3DController::connect_device() { if (m_device != nullptr) diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 64fa34a44..778a3efed 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -19,9 +19,11 @@ class Mouse3DController { class State { + public: static const double DefaultTranslationScale; static const float DefaultRotationScale; + private: mutable std::mutex m_mutex; Vec3d m_translation; @@ -37,6 +39,11 @@ class Mouse3DController void set_translation(const Vec3d& translation); void set_rotation(const Vec3f& rotation); void set_button(unsigned int id); + void reset_buttons(); + + const Vec3d& get_translation() const; + const Vec3f& get_rotation() const; + const std::vector& get_buttons() const; bool has_translation() const; bool has_rotation() const; @@ -54,12 +61,13 @@ class Mouse3DController }; bool m_initialized; - State m_state; + mutable State m_state; std::thread m_thread; GLCanvas3D* m_canvas; - std::mutex m_mutex; + mutable std::mutex m_mutex; hid_device* m_device; bool m_running; + bool m_settings_dialog; public: Mouse3DController(); @@ -87,6 +95,10 @@ public: return (m_canvas != nullptr) ? m_state.apply(*m_canvas) : false; } + bool is_settings_dialog_shown() const { return m_settings_dialog; } + void show_settings_dialog(bool show) { m_settings_dialog = show; } + void render_settings_dialog() const; + private: void connect_device(); void disconnect_device(); From a5543040384541aa0a9ab15cb51d6e3446d36c03 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 3 Oct 2019 10:26:28 +0200 Subject: [PATCH 08/73] ENABLE_3DCONNEXION_DEVICES -> Serialization of mouse device parameters into config --- src/slic3r/GUI/AppConfig.cpp | 42 ++++++++++++++++++++++++++ src/slic3r/GUI/AppConfig.hpp | 6 ++++ src/slic3r/GUI/Mouse3DController.cpp | 44 +++++++++++++++++++++++----- src/slic3r/GUI/Mouse3DController.hpp | 1 + 4 files changed, 86 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp index 5a165e8ae..2d5ee6f5f 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -268,6 +268,48 @@ void AppConfig::set_recent_projects(const std::vector& recent_proje } } +#if ENABLE_3DCONNEXION_DEVICES +void AppConfig::set_mouse_device(const std::string& name, double translation_speed, float rotation_speed) +{ + std::string key = std::string("mouse_device:") + name; + auto it = m_storage.find(key); + if (it == m_storage.end()) + it = m_storage.insert(std::map>::value_type(key, std::map())).first; + + it->second.clear(); + it->second["translation_speed"] = std::to_string(translation_speed); + it->second["rotation_speed"] = std::to_string(rotation_speed); +} + +bool AppConfig::get_mouse_device_translation_speed(const std::string& name, double& translation_speed) +{ + std::string key = std::string("mouse_device:") + name; + auto it = m_storage.find(key); + if (it == m_storage.end()) + return false; + + auto it_val = it->second.find("translation_speed"); + if (it_val == it->second.end()) + return false; + + translation_speed = ::atof(it_val->second.c_str()); +} + +bool AppConfig::get_mouse_device_rotation_speed(const std::string& name, float& rotation_speed) +{ + std::string key = std::string("mouse_device:") + name; + auto it = m_storage.find(key); + if (it == m_storage.end()) + return false; + + auto it_val = it->second.find("rotation_speed"); + if (it_val == it->second.end()) + return false; + + rotation_speed = (float)::atof(it_val->second.c_str()); +} +#endif // ENABLE_3DCONNEXION_DEVICES + void AppConfig::update_config_dir(const std::string &dir) { this->set("recent", "config_directory", dir); diff --git a/src/slic3r/GUI/AppConfig.hpp b/src/slic3r/GUI/AppConfig.hpp index 8ad17b9db..4959b5433 100644 --- a/src/slic3r/GUI/AppConfig.hpp +++ b/src/slic3r/GUI/AppConfig.hpp @@ -125,6 +125,12 @@ public: std::vector get_recent_projects() const; void set_recent_projects(const std::vector& recent_projects); +#if ENABLE_3DCONNEXION_DEVICES + void set_mouse_device(const std::string& name, double translation_speed, float rotation_speed); + bool get_mouse_device_translation_speed(const std::string& name, double& translation_speed); + bool get_mouse_device_rotation_speed(const std::string& name, float& rotation_speed); +#endif // ENABLE_3DCONNEXION_DEVICES + private: // Map of section, name -> value std::map> m_storage; diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 18c17f7c2..c7bf89b4d 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -6,6 +6,7 @@ #include "GLCanvas3D.hpp" #include "GUI_App.hpp" #include "PresetBundle.hpp" +#include "AppConfig.hpp" #include @@ -34,8 +35,6 @@ static const std::vector _3DCONNEXION_DEVICES = 0xc629 // SPACEPILOTPRO = 50729 }; -static const unsigned int _3DCONNEXION_MAX_BUTTONS_COUNT = 256; - namespace Slic3r { namespace GUI { @@ -168,6 +167,7 @@ Mouse3DController::Mouse3DController() : m_initialized(false) , m_canvas(nullptr) , m_device(nullptr) + , m_device_str("") , m_running(false) , m_settings_dialog(false) { @@ -223,15 +223,12 @@ void Mouse3DController::render_settings_dialog() const imgui.begin(_(L("3Dconnexion settings")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); - std::vector product(1024, 0); - hid_get_product_string(m_device, product.data(), 1024); - const ImVec4& color = ImGui::GetStyleColorVec4(ImGuiCol_Separator); ImGui::PushStyleColor(ImGuiCol_Text, color); imgui.text(_(L("Device:"))); ImGui::PopStyleColor(); ImGui::SameLine(); - imgui.text(boost::nowide::narrow(product.data())); + imgui.text(m_device_str); ImGui::Separator(); ImGui::PushStyleColor(ImGuiCol_Text, color); @@ -306,6 +303,22 @@ void Mouse3DController::connect_device() // Open the 3Dconnexion device using the VID, PID m_device = hid_open(vendor_id, product_id, nullptr); + + if (m_device != nullptr) + { + std::vector product(1024, 0); + hid_get_product_string(m_device, product.data(), 1024); + m_device_str = boost::nowide::narrow(product.data()); + + // gets device parameters from the config, if present + double translation = 1.0; + float rotation = 1.0; + wxGetApp().app_config->get_mouse_device_translation_speed(m_device_str, translation); + wxGetApp().app_config->get_mouse_device_rotation_speed(m_device_str, rotation); + // clamp to valid values + m_state.set_translation_scale(State::DefaultTranslationScale * std::max(0.5, std::min(2.0, translation))); + m_state.set_rotation_scale(State::DefaultRotationScale * std::max(0.5f, std::min(2.0f, rotation))); + } } void Mouse3DController::disconnect_device() @@ -313,9 +326,14 @@ void Mouse3DController::disconnect_device() if (m_device == nullptr) return; + // stores current device parameters into the config + wxGetApp().app_config->set_mouse_device(m_device_str, m_state.get_translation_scale() / State::DefaultTranslationScale, m_state.get_rotation_scale() / State::DefaultRotationScale); + wxGetApp().app_config->save(); + // Close the 3Dconnexion device hid_close(m_device); m_device = nullptr; + m_device_str = ""; } void Mouse3DController::start() @@ -396,12 +414,24 @@ void Mouse3DController::collect_input() } case Button: { - for (unsigned int i = 0; i < _3DCONNEXION_MAX_BUTTONS_COUNT; ++i) + // Because of lack of documentation, it is not clear how we should interpret the retrieved data for the button case. + // Experiments made with SpaceNavigator: + // retrieved_data[1] == 0 if no button pressed + // retrieved_data[1] == 1 if left button pressed + // retrieved_data[1] == 2 if right button pressed + // retrieved_data[1] == 3 if left and right button pressed + // seems to show that each button is associated to a bit of retrieved_data[1], which means that at max 8 buttons can be supported. + for (unsigned int i = 0; i < 8; ++i) { if (retrieved_data[1] & (0x1 << i)) m_state.set_button(i); } +// // On the other hand, other libraries, as in https://github.com/koenieee/CrossplatformSpacemouseDriver/blob/master/SpaceMouseDriver/driver/SpaceMouseController.cpp +// // interpret retrieved_data[1] as the button id +// if (retrieved_data[1] > 0) +// m_state.set_button((unsigned int)retrieved_data[1]); + break; } default: diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 778a3efed..55deaf118 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -66,6 +66,7 @@ class Mouse3DController GLCanvas3D* m_canvas; mutable std::mutex m_mutex; hid_device* m_device; + std::string m_device_str; bool m_running; bool m_settings_dialog; From 587effbedf754855134c52bb4aa1c6fb447ebb61 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 3 Oct 2019 11:38:31 +0200 Subject: [PATCH 09/73] ENABLE_3DCONNEXION_DEVICES -> Refactored Mouse3DController to be unaware of current active GLCanvas3D --- src/slic3r/GUI/Camera.cpp | 20 ++++++++++++++++ src/slic3r/GUI/Camera.hpp | 5 ++++ src/slic3r/GUI/GLCanvas3D.cpp | 36 ++++++++++++++++++++++++++-- src/slic3r/GUI/GLCanvas3D.hpp | 5 ++++ src/slic3r/GUI/MainFrame.cpp | 4 ---- src/slic3r/GUI/Mouse3DController.cpp | 19 ++++++--------- src/slic3r/GUI/Mouse3DController.hpp | 20 ++++------------ src/slic3r/GUI/Plater.cpp | 8 ------- 8 files changed, 75 insertions(+), 42 deletions(-) diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index 8e3a6d1f1..4bb3e6ad5 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -103,6 +103,25 @@ void Camera::set_theta(float theta, bool apply_limit) } } +#if ENABLE_3DCONNEXION_DEVICES +void Camera::update_zoom(double delta_zoom) +{ + set_zoom(m_zoom / (1.0 - std::max(std::min(delta_zoom, 4.0), -4.0) * 0.1)); +} + +void Camera::set_zoom(double zoom) +{ + // Don't allow to zoom too far outside the scene. + double zoom_min = calc_zoom_to_bounding_box_factor(m_scene_box, (int)m_viewport[2], (int)m_viewport[3]); + if (zoom_min > 0.0) + zoom = std::max(zoom, zoom_min * 0.7); + + // Don't allow to zoom too close to the scene. + zoom = std::min(zoom, 100.0); + + m_zoom = zoom; +} +#else void Camera::set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h) { zoom = std::max(std::min(zoom, 4.0), -4.0) / 10.0; @@ -118,6 +137,7 @@ void Camera::set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, i m_zoom = zoom; } +#endif // ENABLE_3DCONNEXION_DEVICES bool Camera::select_view(const std::string& direction) { diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp index 839d0d6cf..bf452833c 100644 --- a/src/slic3r/GUI/Camera.hpp +++ b/src/slic3r/GUI/Camera.hpp @@ -63,8 +63,13 @@ public: void set_theta(float theta, bool apply_limit); double get_zoom() const { return m_zoom; } +#if ENABLE_3DCONNEXION_DEVICES + void update_zoom(double delta_zoom); + void set_zoom(double zoom); +#else void set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h); void set_zoom(double zoom) { m_zoom = zoom; } +#endif // ENABLE_3DCONNEXION_DEVICES const BoundingBoxf3& get_scene_box() const { return m_scene_box; } void set_scene_box(const BoundingBoxf3& box) { m_scene_box = box; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 562a939fb..6f94647ac 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1394,7 +1394,11 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const BoundingBoxf3 GLCanvas3D::scene_bounding_box() const { BoundingBoxf3 bb = volumes_bounding_box(); +#if ENABLE_3DCONNEXION_DEVICES + bb.merge(m_bed.get_bounding_box(true)); +#else bb.merge(m_bed.get_bounding_box(false)); +#endif // ENABLE_3DCONNEXION_DEVICES if (m_config != nullptr) { @@ -1542,10 +1546,16 @@ void GLCanvas3D::render() return; } +#if ENABLE_3DCONNEXION_DEVICES + const Size& cnv_size = get_canvas_size(); +#endif // ENABLE_3DCONNEXION_DEVICES + if (m_camera.requires_zoom_to_bed) { zoom_to_bed(); +#if !ENABLE_3DCONNEXION_DEVICES const Size& cnv_size = get_canvas_size(); +#endif // !ENABLE_3DCONNEXION_DEVICES _resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); m_camera.requires_zoom_to_bed = false; } @@ -1637,7 +1647,7 @@ void GLCanvas3D::render() #endif // ENABLE_CAMERA_STATISTICS #if ENABLE_3DCONNEXION_DEVICES - wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog(); + wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); #endif // ENABLE_3DCONNEXION_DEVICES wxGetApp().imgui()->render(); @@ -2313,7 +2323,7 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) m_dirty |= m_undoredo_toolbar.update_items_state(); m_dirty |= m_view_toolbar.update_items_state(); #if ENABLE_3DCONNEXION_DEVICES - bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(); + bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(m_camera); m_dirty |= mouse3d_controller_applied; #endif // ENABLE_3DCONNEXION_DEVICES @@ -2460,11 +2470,19 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case 'B': case 'b': { zoom_to_bed(); break; } case 'I': +#if ENABLE_3DCONNEXION_DEVICES + case 'i': { _update_camera_zoom(1.0); break; } +#else case 'i': { set_camera_zoom(1.0); break; } +#endif // ENABLE_3DCONNEXION_DEVICES case 'K': case 'k': { m_camera.select_next_type(); m_dirty = true; break; } case 'O': +#if ENABLE_3DCONNEXION_DEVICES + case 'o': { _update_camera_zoom(-1.0); break; } +#else case 'o': { set_camera_zoom(-1.0); break; } +#endif // ENABLE_3DCONNEXION_DEVICES #if ENABLE_RENDER_PICKING_PASS case 'T': case 't': { @@ -2618,7 +2636,11 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) return; // Calculate the zoom delta and apply it to the current zoom factor +#if ENABLE_3DCONNEXION_DEVICES + _update_camera_zoom((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta()); +#else set_camera_zoom((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta()); +#endif // ENABLE_3DCONNEXION_DEVICES } void GLCanvas3D::on_timer(wxTimerEvent& evt) @@ -3413,12 +3435,14 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type) m_dirty = true; } +#if !ENABLE_3DCONNEXION_DEVICES void GLCanvas3D::set_camera_zoom(double zoom) { const Size& cnv_size = get_canvas_size(); m_camera.set_zoom(zoom, _max_bounding_box(false, true), cnv_size.get_width(), cnv_size.get_height()); m_dirty = true; } +#endif // !ENABLE_3DCONNEXION_DEVICES void GLCanvas3D::update_gizmos_on_off_state() { @@ -3887,6 +3911,14 @@ void GLCanvas3D::_zoom_to_box(const BoundingBoxf3& box) m_dirty = true; } +#if ENABLE_3DCONNEXION_DEVICES +void GLCanvas3D::_update_camera_zoom(double zoom) +{ + m_camera.update_zoom(zoom); + m_dirty = true; +} +#endif // ENABLE_3DCONNEXION_DEVICES + void GLCanvas3D::_refresh_if_shown_on_screen() { if (_is_shown_on_screen()) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index cce6b0f2b..3051ae653 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -567,7 +567,9 @@ public: void do_flatten(const Vec3d& normal, const std::string& snapshot_type); void do_mirror(const std::string& snapshot_type); +#if !ENABLE_3DCONNEXION_DEVICES void set_camera_zoom(double zoom); +#endif // !ENABLE_3DCONNEXION_DEVICES void update_gizmos_on_off_state(); void reset_all_gizmos() { m_gizmos.reset_all_states(); } @@ -640,6 +642,9 @@ private: BoundingBoxf3 _max_bounding_box(bool include_gizmos, bool include_bed_model) const; void _zoom_to_box(const BoundingBoxf3& box); +#if ENABLE_3DCONNEXION_DEVICES + void _update_camera_zoom(double zoom); +#endif // ENABLE_3DCONNEXION_DEVICES void _refresh_if_shown_on_screen(); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 615d7d8a9..eaf6944b6 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -111,10 +111,6 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S } if(m_plater) m_plater->stop_jobs(); -#if ENABLE_3DCONNEXION_DEVICES - if (m_plater != nullptr) - m_plater->get_mouse3d_controller().set_canvas(nullptr); -#endif // ENABLE_3DCONNEXION_DEVICES // Weird things happen as the Paint messages are floating around the windows being destructed. // Avoid the Paint messages by hiding the main window. diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index c7bf89b4d..ccf1adfa2 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -3,7 +3,7 @@ #if ENABLE_3DCONNEXION_DEVICES -#include "GLCanvas3D.hpp" +#include "Camera.hpp" #include "GUI_App.hpp" #include "PresetBundle.hpp" #include "AppConfig.hpp" @@ -115,13 +115,12 @@ bool Mouse3DController::State::has_any_button() const return !m_buttons.empty(); } -bool Mouse3DController::State::apply(GLCanvas3D& canvas) +bool Mouse3DController::State::apply(Camera& camera) { if (!wxGetApp().IsActive()) return false; bool ret = false; - Camera& camera = canvas.get_camera(); if (has_translation()) { @@ -150,8 +149,8 @@ bool Mouse3DController::State::apply(GLCanvas3D& canvas) { switch (i) { - case 0: { canvas.set_camera_zoom(1.0); break; } - case 1: { canvas.set_camera_zoom(-1.0); break; } + case 0: { camera.update_zoom(1.0); break; } + case 1: { camera.update_zoom(-1.0); break; } default: { break; } } } @@ -165,7 +164,6 @@ bool Mouse3DController::State::apply(GLCanvas3D& canvas) Mouse3DController::Mouse3DController() : m_initialized(false) - , m_canvas(nullptr) , m_device(nullptr) , m_device_str("") , m_running(false) @@ -206,17 +204,14 @@ void Mouse3DController::shutdown() m_initialized = false; } -void Mouse3DController::render_settings_dialog() const +void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const { - if ((m_canvas == nullptr) || !m_running || !m_settings_dialog) + if (!m_running || !m_settings_dialog) return; ImGuiWrapper& imgui = *wxGetApp().imgui(); - std::lock_guard lock(m_mutex); - Size cnv_size = m_canvas->get_canvas_size(); - - imgui.set_next_window_pos(0.5f * (float)cnv_size.get_width(), 0.5f * (float)cnv_size.get_height(), ImGuiCond_Always, 0.5f, 0.5f); + imgui.set_next_window_pos(0.5f * (float)canvas_width, 0.5f * (float)canvas_height, ImGuiCond_Always, 0.5f, 0.5f); imgui.set_next_window_bg_alpha(0.5f); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 55deaf118..bfad1b3b2 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -13,7 +13,7 @@ namespace Slic3r { namespace GUI { -class GLCanvas3D; +struct Camera; class Mouse3DController { @@ -57,14 +57,12 @@ class Mouse3DController void set_rotation_scale(float scale) { m_rotation_scale = scale; } // return true if any change to the camera took place - bool apply(GLCanvas3D& canvas); + bool apply(Camera& camera); }; bool m_initialized; mutable State m_state; std::thread m_thread; - GLCanvas3D* m_canvas; - mutable std::mutex m_mutex; hid_device* m_device; std::string m_device_str; bool m_running; @@ -79,26 +77,16 @@ public: bool is_device_connected() const { return m_device != nullptr; } bool is_running() const { return m_running; } - void set_canvas(GLCanvas3D* canvas) - { - std::lock_guard lock(m_mutex); - m_canvas = canvas; - } - bool has_translation() const { return m_state.has_translation(); } bool has_rotation() const { return m_state.has_rotation(); } bool has_translation_or_rotation() const { return m_state.has_translation_or_rotation(); } bool has_any_button() const { return m_state.has_any_button(); } - bool apply() - { - std::lock_guard lock(m_mutex); - return (m_canvas != nullptr) ? m_state.apply(*m_canvas) : false; - } + bool apply(Camera& camera) { return m_state.apply(camera); } bool is_settings_dialog_shown() const { return m_settings_dialog; } void show_settings_dialog(bool show) { m_settings_dialog = show; } - void render_settings_dialog() const; + void render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const; private: void connect_device(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c3214336b..b85da3373 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3263,10 +3263,6 @@ void Plater::priv::set_current_panel(wxPanel* panel) view3D->reload_scene(true); } -#if ENABLE_3DCONNEXION_DEVICES - mouse3d_controller.set_canvas(view3D->get_canvas3d()); -#endif // ENABLE_3DCONNEXION_DEVICES - // sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) view3D->set_as_dirty(); view_toolbar.select_item("3D"); @@ -3282,10 +3278,6 @@ void Plater::priv::set_current_panel(wxPanel* panel) // keeps current gcode preview, if any preview->reload_print(true); -#if ENABLE_3DCONNEXION_DEVICES - mouse3d_controller.set_canvas(preview->get_canvas3d()); -#endif // ENABLE_3DCONNEXION_DEVICES - preview->set_canvas_as_dirty(); view_toolbar.select_item("Preview"); } From b15757a126f23bae68221243c94faf62a0134f47 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 3 Oct 2019 12:09:49 +0200 Subject: [PATCH 10/73] ENABLE_3DCONNEXION_DEVICES - Added missing return lines --- src/slic3r/GUI/AppConfig.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp index 2d5ee6f5f..6d1c9cddd 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -293,6 +293,7 @@ bool AppConfig::get_mouse_device_translation_speed(const std::string& name, doub return false; translation_speed = ::atof(it_val->second.c_str()); + return true; } bool AppConfig::get_mouse_device_rotation_speed(const std::string& name, float& rotation_speed) @@ -307,6 +308,7 @@ bool AppConfig::get_mouse_device_rotation_speed(const std::string& name, float& return false; rotation_speed = (float)::atof(it_val->second.c_str()); + return true; } #endif // ENABLE_3DCONNEXION_DEVICES From f31568180465b8c8662a2569942998f697d281b2 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 3 Oct 2019 12:16:59 +0200 Subject: [PATCH 11/73] ENABLE_3DCONNEXION_DEVICES -> Refactored Mouse3DController to simplify code --- src/slic3r/GUI/Mouse3DController.cpp | 89 +++++----------------------- src/slic3r/GUI/Mouse3DController.hpp | 27 ++++----- 2 files changed, 27 insertions(+), 89 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index ccf1adfa2..f4e57b372 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -49,72 +49,6 @@ Mouse3DController::State::State() { } -void Mouse3DController::State::set_translation(const Vec3d& translation) -{ - std::lock_guard lock(m_mutex); - m_translation = translation; -} - -void Mouse3DController::State::set_rotation(const Vec3f& rotation) -{ - std::lock_guard lock(m_mutex); - m_rotation = rotation; -} - -void Mouse3DController::State::set_button(unsigned int id) -{ - std::lock_guard lock(m_mutex); - m_buttons.push_back(id); -} - -void Mouse3DController::State::reset_buttons() -{ - std::lock_guard lock(m_mutex); - return m_buttons.clear(); -} - -const Vec3d& Mouse3DController::State::get_translation() const -{ - std::lock_guard lock(m_mutex); - return m_translation; -} - -const Vec3f& Mouse3DController::State::get_rotation() const -{ - std::lock_guard lock(m_mutex); - return m_rotation; -} - -const std::vector& Mouse3DController::State::get_buttons() const -{ - std::lock_guard lock(m_mutex); - return m_buttons; -} - -bool Mouse3DController::State::has_translation() const -{ - std::lock_guard lock(m_mutex); - return !m_translation.isApprox(Vec3d::Zero()); -} - -bool Mouse3DController::State::has_rotation() const -{ - std::lock_guard lock(m_mutex); - return !m_rotation.isApprox(Vec3f::Zero()); -} - -bool Mouse3DController::State::has_translation_or_rotation() const -{ - std::lock_guard lock(m_mutex); - return !m_translation.isApprox(Vec3d::Zero()) && !m_rotation.isApprox(Vec3f::Zero()); -} - -bool Mouse3DController::State::has_any_button() const -{ - std::lock_guard lock(m_mutex); - return !m_buttons.empty(); -} - bool Mouse3DController::State::apply(Camera& camera) { if (!wxGetApp().IsActive()) @@ -124,28 +58,25 @@ bool Mouse3DController::State::apply(Camera& camera) if (has_translation()) { - Vec3d translation = get_translation(); - camera.set_target(camera.get_target() + m_translation_scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up())); - set_translation(Vec3d::Zero()); + camera.set_target(camera.get_target() + m_translation_scale * (m_translation(0) * camera.get_dir_right() + m_translation(1) * camera.get_dir_forward() + m_translation(2) * camera.get_dir_up())); + m_translation = Vec3d::Zero(); ret = true; } if (has_rotation()) { - Vec3f rotation = get_rotation(); - float theta = m_rotation_scale * rotation(0); - float phi = m_rotation_scale * rotation(2); + float theta = m_rotation_scale * m_rotation(0); + float phi = m_rotation_scale * m_rotation(2); float sign = camera.inverted_phi ? -1.0f : 1.0f; camera.phi += sign * phi; camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); - set_rotation(Vec3f::Zero()); + m_rotation = Vec3f::Zero(); ret = true; } if (has_any_button()) { - std::vector buttons = get_buttons(); - for (unsigned int i : buttons) + for (unsigned int i : m_buttons) { switch (i) { @@ -204,6 +135,12 @@ void Mouse3DController::shutdown() m_initialized = false; } +bool Mouse3DController::apply(Camera& camera) +{ + std::lock_guard lock(m_mutex); + return m_state.apply(camera); +} + void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const { if (!m_running || !m_settings_dialog) @@ -383,6 +320,8 @@ void Mouse3DController::collect_input() return; } + std::lock_guard lock(m_mutex); + if (res > 0) { switch (retrieved_data[0]) diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index bfad1b3b2..8abc4299c 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -24,8 +24,6 @@ class Mouse3DController static const float DefaultRotationScale; private: - mutable std::mutex m_mutex; - Vec3d m_translation; Vec3f m_rotation; std::vector m_buttons; @@ -36,19 +34,19 @@ class Mouse3DController public: State(); - void set_translation(const Vec3d& translation); - void set_rotation(const Vec3f& rotation); - void set_button(unsigned int id); - void reset_buttons(); + void set_translation(const Vec3d& translation) { m_translation = translation; } + void set_rotation(const Vec3f& rotation) { m_rotation = rotation; } + void set_button(unsigned int id) { m_buttons.push_back(id); } + void reset_buttons() { return m_buttons.clear(); } - const Vec3d& get_translation() const; - const Vec3f& get_rotation() const; - const std::vector& get_buttons() const; + const Vec3d& get_translation() const { return m_translation; } + const Vec3f& get_rotation() const { return m_rotation; } + const std::vector& get_buttons() const { return m_buttons; } - bool has_translation() const; - bool has_rotation() const; - bool has_translation_or_rotation() const; - bool has_any_button() const; + bool has_translation() const { return !m_translation.isApprox(Vec3d::Zero()); } + bool has_rotation() const { return !m_rotation.isApprox(Vec3f::Zero()); } + bool has_translation_or_rotation() const { return !m_translation.isApprox(Vec3d::Zero()) && !m_rotation.isApprox(Vec3f::Zero()); } + bool has_any_button() const { return !m_buttons.empty(); } double get_translation_scale() const { return m_translation_scale; } void set_translation_scale(double scale) { m_translation_scale = scale; } @@ -62,6 +60,7 @@ class Mouse3DController bool m_initialized; mutable State m_state; + std::mutex m_mutex; std::thread m_thread; hid_device* m_device; std::string m_device_str; @@ -82,7 +81,7 @@ public: bool has_translation_or_rotation() const { return m_state.has_translation_or_rotation(); } bool has_any_button() const { return m_state.has_any_button(); } - bool apply(Camera& camera) { return m_state.apply(camera); } + bool apply(Camera& camera); bool is_settings_dialog_shown() const { return m_settings_dialog; } void show_settings_dialog(bool show) { m_settings_dialog = show; } From 1d463fc23f9bcd2d24a26a00f845ca42c3aa3a6c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 4 Oct 2019 07:58:01 +0200 Subject: [PATCH 12/73] ENABLE_3DCONNEXION_DEVICES -> Another small refactoring of Mouse3DController --- src/slic3r/GUI/Mouse3DController.cpp | 44 +++++++++++++--------------- src/slic3r/GUI/Mouse3DController.hpp | 4 --- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index f4e57b372..660d8d809 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -285,24 +285,22 @@ void Mouse3DController::run() } } -double convert_input(int first, unsigned char val) -{ - int ret = 0; - - switch (val) - { - case 0: { ret = first; break; } - case 1: { ret = first + 255; break; } - case 254: { ret = -511 + first; break; } - case 255: { ret = -255 + first; break; } - default: { break; } - } - - return (double)ret / 349.0; -} - void Mouse3DController::collect_input() { + auto convert_input = [](unsigned char first, unsigned char second)-> double + { + int ret = 0; + switch (second) + { + case 0: { ret = (int)first; break; } + case 1: { ret = (int)first + 255; break; } + case 254: { ret = -511 + (int)first; break; } + case 255: { ret = -255 + (int)first; break; } + default: { break; } + } + return (double)ret / 349.0; + }; + // Read data from device enum EDataType { @@ -328,19 +326,19 @@ void Mouse3DController::collect_input() { case Translation: { - Vec3d translation(-convert_input((int)retrieved_data[1], retrieved_data[2]), - convert_input((int)retrieved_data[3], retrieved_data[4]), - convert_input((int)retrieved_data[5], retrieved_data[6])); - if (!translation.isApprox(Vec3d::Zero())) + Vec3d translation(-convert_input(retrieved_data[1], retrieved_data[2]), + convert_input(retrieved_data[3], retrieved_data[4]), + convert_input(retrieved_data[5], retrieved_data[6])); + if (!translation.isApprox(Vec3d::Zero())) m_state.set_translation(translation); break; } case Rotation: { - Vec3f rotation(-(float)convert_input((int)retrieved_data[1], retrieved_data[2]), - (float)convert_input((int)retrieved_data[3], retrieved_data[4]), - -(float)convert_input((int)retrieved_data[5], retrieved_data[6])); + Vec3f rotation(-(float)convert_input(retrieved_data[1], retrieved_data[2]), + (float)convert_input(retrieved_data[3], retrieved_data[4]), + -(float)convert_input(retrieved_data[5], retrieved_data[6])); if (!rotation.isApprox(Vec3f::Zero())) m_state.set_rotation(rotation); diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 8abc4299c..bb15d4351 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -39,10 +39,6 @@ class Mouse3DController void set_button(unsigned int id) { m_buttons.push_back(id); } void reset_buttons() { return m_buttons.clear(); } - const Vec3d& get_translation() const { return m_translation; } - const Vec3f& get_rotation() const { return m_rotation; } - const std::vector& get_buttons() const { return m_buttons; } - bool has_translation() const { return !m_translation.isApprox(Vec3d::Zero()); } bool has_rotation() const { return !m_rotation.isApprox(Vec3f::Zero()); } bool has_translation_or_rotation() const { return !m_translation.isApprox(Vec3d::Zero()) && !m_rotation.isApprox(Vec3f::Zero()); } From 5a94ac94999d2f85f8103b3ed2b7162c35a9975f Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 4 Oct 2019 10:59:27 +0200 Subject: [PATCH 13/73] ENABLE_3DCONNEXION_DEVICES -> Automatic detection of plugging/unplugging 3Dconnexion devices --- src/slic3r/GUI/Mouse3DController.cpp | 61 ++++++++++++++++++++-------- src/slic3r/GUI/Mouse3DController.hpp | 11 ++--- 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 660d8d809..d7e9bda4a 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -11,6 +11,7 @@ #include #include +#include #include "I18N.hpp" @@ -110,12 +111,12 @@ void Mouse3DController::init() // Initialize the hidapi library int res = hid_init(); if (res != 0) + { + BOOST_LOG_TRIVIAL(error) << "Unable to initialize hidapi library"; return; + } m_initialized = true; - - connect_device(); - start(); } void Mouse3DController::shutdown() @@ -124,10 +125,6 @@ void Mouse3DController::shutdown() return; stop(); - - if (m_thread.joinable()) - m_thread.join(); - disconnect_device(); // Finalize the hidapi library @@ -137,8 +134,24 @@ void Mouse3DController::shutdown() bool Mouse3DController::apply(Camera& camera) { + if (!m_initialized) + return false; + std::lock_guard lock(m_mutex); - return m_state.apply(camera); + + // check if the user unplugged the device + if (!m_running && is_device_connected()) + { + disconnect_device(); + // hides the settings dialog if the user re-plug the device + m_settings_dialog = false; + } + + // check if the user plugged the device + if (connect_device()) + start(); + + return is_device_connected() ? m_state.apply(camera) : false; } void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const @@ -180,15 +193,18 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign ImGui::PopStyleVar(); } -void Mouse3DController::connect_device() +bool Mouse3DController::connect_device() { - if (m_device != nullptr) - return; + if (is_device_connected()) + return false; // Enumerates devices hid_device_info* devices = hid_enumerate(0, 0); if (devices == nullptr) - return; + { + BOOST_LOG_TRIVIAL(error) << "Unable to enumerate HID devices"; + return false; + } // Searches for 1st connected 3Dconnexion device unsigned short vendor_id = 0; @@ -231,7 +247,7 @@ void Mouse3DController::connect_device() hid_free_enumeration(devices); if (vendor_id == 0) - return; + return false; // Open the 3Dconnexion device using the VID, PID m_device = hid_open(vendor_id, product_id, nullptr); @@ -242,7 +258,9 @@ void Mouse3DController::connect_device() hid_get_product_string(m_device, product.data(), 1024); m_device_str = boost::nowide::narrow(product.data()); - // gets device parameters from the config, if present + BOOST_LOG_TRIVIAL(info) << "Connected device: " << m_device_str; + + // get device parameters from the config, if present double translation = 1.0; float rotation = 1.0; wxGetApp().app_config->get_mouse_device_translation_speed(m_device_str, translation); @@ -251,26 +269,35 @@ void Mouse3DController::connect_device() m_state.set_translation_scale(State::DefaultTranslationScale * std::max(0.5, std::min(2.0, translation))); m_state.set_rotation_scale(State::DefaultRotationScale * std::max(0.5f, std::min(2.0f, rotation))); } + + return (m_device != nullptr); } void Mouse3DController::disconnect_device() { - if (m_device == nullptr) + if (!is_device_connected()) return; - // stores current device parameters into the config + // Stop the secondary thread, if running + if (m_thread.joinable()) + m_thread.join(); + + // Store current device parameters into the config wxGetApp().app_config->set_mouse_device(m_device_str, m_state.get_translation_scale() / State::DefaultTranslationScale, m_state.get_rotation_scale() / State::DefaultRotationScale); wxGetApp().app_config->save(); // Close the 3Dconnexion device hid_close(m_device); m_device = nullptr; + + BOOST_LOG_TRIVIAL(info) << "Disconnected device: " << m_device_str; + m_device_str = ""; } void Mouse3DController::start() { - if ((m_device == nullptr) || m_running) + if (!is_device_connected() || m_running) return; m_thread = std::thread(&Mouse3DController::run, this); diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index bb15d4351..050ef436e 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -72,23 +72,24 @@ public: bool is_device_connected() const { return m_device != nullptr; } bool is_running() const { return m_running; } - bool has_translation() const { return m_state.has_translation(); } - bool has_rotation() const { return m_state.has_rotation(); } +// bool has_translation() const { return m_state.has_translation(); } +// bool has_rotation() const { return m_state.has_rotation(); } bool has_translation_or_rotation() const { return m_state.has_translation_or_rotation(); } - bool has_any_button() const { return m_state.has_any_button(); } +// bool has_any_button() const { return m_state.has_any_button(); } bool apply(Camera& camera); bool is_settings_dialog_shown() const { return m_settings_dialog; } - void show_settings_dialog(bool show) { m_settings_dialog = show; } + void show_settings_dialog(bool show) { m_settings_dialog = show && is_running(); } void render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const; private: - void connect_device(); + bool connect_device(); void disconnect_device(); void start(); void stop() { m_running = false; } + // secondary thread methods void run(); void collect_input(); }; From 0ba9fbaf0c568f5f377bac6579ae693f856cfa7a Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 7 Oct 2019 09:31:23 +0200 Subject: [PATCH 14/73] ENABLE_3DCONNEXION_DEVICES -> Update 3D scene only when mouse 3D device send update messages --- src/slic3r/GUI/GLCanvas3D.cpp | 5 ++--- src/slic3r/GUI/Mouse3DController.cpp | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 6f94647ac..7b2b5f6d4 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2323,8 +2323,7 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) m_dirty |= m_undoredo_toolbar.update_items_state(); m_dirty |= m_view_toolbar.update_items_state(); #if ENABLE_3DCONNEXION_DEVICES - bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(m_camera); - m_dirty |= mouse3d_controller_applied; + m_dirty |= wxGetApp().plater()->get_mouse3d_controller().apply(m_camera); #endif // ENABLE_3DCONNEXION_DEVICES if (!m_dirty) @@ -2333,7 +2332,7 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) _refresh_if_shown_on_screen(); #if ENABLE_3DCONNEXION_DEVICES - if (m_keep_dirty || wxGetApp().plater()->get_mouse3d_controller().is_device_connected()) + if (m_keep_dirty) { m_dirty = true; evt.RequestMore(); diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index d7e9bda4a..f32f54b2d 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -349,6 +349,8 @@ void Mouse3DController::collect_input() if (res > 0) { + bool updated = false; + switch (retrieved_data[0]) { case Translation: @@ -356,8 +358,11 @@ void Mouse3DController::collect_input() Vec3d translation(-convert_input(retrieved_data[1], retrieved_data[2]), convert_input(retrieved_data[3], retrieved_data[4]), convert_input(retrieved_data[5], retrieved_data[6])); - if (!translation.isApprox(Vec3d::Zero())) + if (!translation.isApprox(Vec3d::Zero())) + { + updated = true; m_state.set_translation(translation); + } break; } @@ -367,7 +372,10 @@ void Mouse3DController::collect_input() (float)convert_input(retrieved_data[3], retrieved_data[4]), -(float)convert_input(retrieved_data[5], retrieved_data[6])); if (!rotation.isApprox(Vec3f::Zero())) + { + updated = true; m_state.set_rotation(rotation); + } break; } @@ -383,7 +391,10 @@ void Mouse3DController::collect_input() for (unsigned int i = 0; i < 8; ++i) { if (retrieved_data[1] & (0x1 << i)) + { + updated = true; m_state.set_button(i); + } } // // On the other hand, other libraries, as in https://github.com/koenieee/CrossplatformSpacemouseDriver/blob/master/SpaceMouseDriver/driver/SpaceMouseController.cpp @@ -396,6 +407,10 @@ void Mouse3DController::collect_input() default: break; } + + if (updated) + // ask for an idle event to update 3D scene + wxWakeUpIdle(); } } From 8fcd4e4407a747d5ac5cf46cfcf6c027f9ff825b Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 8 Oct 2019 09:52:56 +0200 Subject: [PATCH 15/73] ENABLE_3DCONNEXION_DEVICES -> added debug code to get 3Cconnexion device product id --- src/slic3r/GUI/Mouse3DController.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 2bd6d9020..854d67862 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -26,21 +26,21 @@ static const std::vector _3DCONNEXION_VENDORS = static const std::vector _3DCONNEXION_DEVICES = { 0xC623, // TRAVELER = 50723 - 0xC626, // NAVIGATOR = 50726 + 0xC626, // NAVIGATOR = 50726 *TESTED* 0xc628, // NAVIGATOR_FOR_NOTEBOOKS = 50728 0xc627, // SPACEEXPLORER = 50727 0xC603, // SPACEMOUSE = 50691 0xC62B, // SPACEMOUSEPRO = 50731 0xc621, // SPACEBALL5000 = 50721 0xc625, // SPACEPILOT = 50725 - 0xc629 // SPACEPILOTPRO = 50729 + 0xc652, // SPACEMOUSE PRO WIRELESS = 50770 *TESTED* }; namespace Slic3r { namespace GUI { const double Mouse3DController::State::DefaultTranslationScale = 2.5; -const float Mouse3DController::State::DefaultRotationScale = 1.0; +const float Mouse3DController::State::DefaultRotationScale = 1.0f; Mouse3DController::State::State() : m_translation_scale(DefaultTranslationScale) @@ -189,8 +189,8 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign ImGui::PopStyleColor(); ImGui::SameLine(); imgui.text(m_device_str); - ImGui::Separator(); + ImGui::Separator(); ImGui::PushStyleColor(ImGuiCol_Text, color); imgui.text(_(L("Speed:"))); ImGui::PopStyleColor(); @@ -199,7 +199,7 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign if (ImGui::SliderFloat(_(L("Translation")), &translation, 0.5f, 2.0f, "%.1f")) m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation); - float rotation = (float)m_state.get_rotation_scale() / State::DefaultRotationScale; + float rotation = m_state.get_rotation_scale() / State::DefaultRotationScale; if (ImGui::SliderFloat(_(L("Rotation")), &rotation, 0.5f, 2.0f, "%.1f")) m_state.set_rotation_scale(State::DefaultRotationScale * rotation); @@ -228,6 +228,17 @@ bool Mouse3DController::connect_device() hid_device_info* current = devices; while (current != nullptr) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + if (current->manufacturer_string != nullptr) + { + std::string aaa = boost::nowide::narrow(current->manufacturer_string); + if (aaa == "3Dconnexion") + { + std::cout << "Detected 3Dconnexion device code: " << current->product_id << " (" << std::hex << current->product_id << std::dec << ")" << std::endl; + } + } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + for (size_t i = 0; i < _3DCONNEXION_VENDORS.size(); ++i) { if (_3DCONNEXION_VENDORS[i] == current->vendor_id) From 9447d3e1b529b246fc8be183cc7cc9be929f4a71 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 8 Oct 2019 13:38:08 +0200 Subject: [PATCH 16/73] ENABLE_3DCONNEXION_DEVICES -> Mouse3DController reworked to handle reports of length 7 bytes and 13 bytes --- src/slic3r/GUI/Mouse3DController.cpp | 288 +++++++++++++++++---------- src/slic3r/GUI/Mouse3DController.hpp | 7 + 2 files changed, 188 insertions(+), 107 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 854d67862..c62b8c851 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -22,18 +22,30 @@ static const std::vector _3DCONNEXION_VENDORS = 0x256F // 3DCONNECTION = 9583 // 3Dconnexion }; -// See: https://github.com/FreeSpacenav/spacenavd/blob/a9eccf34e7cac969ee399f625aef827f4f4aaec6/src/dev.c#L202 for a wider list of devices +// See: https://github.com/FreeSpacenav/spacenavd/blob/a9eccf34e7cac969ee399f625aef827f4f4aaec6/src/dev.c#L202 static const std::vector _3DCONNEXION_DEVICES = { - 0xC623, // TRAVELER = 50723 - 0xC626, // NAVIGATOR = 50726 *TESTED* - 0xc628, // NAVIGATOR_FOR_NOTEBOOKS = 50728 - 0xc627, // SPACEEXPLORER = 50727 - 0xC603, // SPACEMOUSE = 50691 - 0xC62B, // SPACEMOUSEPRO = 50731 - 0xc621, // SPACEBALL5000 = 50721 - 0xc625, // SPACEPILOT = 50725 - 0xc652, // SPACEMOUSE PRO WIRELESS = 50770 *TESTED* + 0xc603, /* 50691 spacemouse plus XT */ + 0xc605, /* 50693 cadman */ + 0xc606, /* 50694 spacemouse classic */ + 0xc621, /* 50721 spaceball 5000 */ + 0xc623, /* 50723 space traveller */ + 0xc625, /* 50725 space pilot */ + 0xc626, /* 50726 space navigator *TESTED* */ + 0xc627, /* 50727 space explorer */ + 0xc628, /* 50728 space navigator for notebooks*/ + 0xc629, /* 50729 space pilot pro*/ + 0xc62b, /* 50731 space mouse pro*/ + 0xc62e, /* 50734 spacemouse wireless (USB cable) */ + 0xc62f, /* 50735 spacemouse wireless receiver */ + 0xc631, /* 50737 spacemouse pro wireless *TESTED* */ + 0xc632, /* 50738 spacemouse pro wireless receiver */ + 0xc633, /* 50739 spacemouse enterprise */ + 0xc635, /* 50741 spacemouse compact */ + 0xc636, /* 50742 spacemouse module */ + 0xc640, /* 50752 nulooq */ + +// 0xc652, /* 50770 3Dconnexion universal receiver */ }; namespace Slic3r { @@ -51,18 +63,18 @@ Mouse3DController::State::State() bool Mouse3DController::State::process_mouse_wheel() { - if (m_mouse_wheel_counter > 0) + if (m_mouse_wheel_counter == 0) + return false; + else if (!m_rotation.empty()) { --m_mouse_wheel_counter; return true; } - else if (m_rotation.empty()) + else { m_mouse_wheel_counter = 0; return true; } - - return false; } bool Mouse3DController::State::apply(Camera& camera) @@ -225,20 +237,29 @@ bool Mouse3DController::connect_device() unsigned short vendor_id = 0; unsigned short product_id = 0; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + hid_device_info* cur = devices; + while (cur != nullptr) + { + std::cout << "Detected device '"; + + if (cur->manufacturer_string != nullptr) + std::wcout << cur->manufacturer_string << L" "; + else + std::wcout << "MMM "; + if (cur->product_string != nullptr) + std::wcout << cur->product_string; + else + std::wcout << "PPP"; + std::cout << "' code: " << cur->product_id << " (" << std::hex << cur->product_id << std::dec << ")" << std::endl; + + cur = cur->next; + } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + hid_device_info* current = devices; while (current != nullptr) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - if (current->manufacturer_string != nullptr) - { - std::string aaa = boost::nowide::narrow(current->manufacturer_string); - if (aaa == "3Dconnexion") - { - std::cout << "Detected 3Dconnexion device code: " << current->product_id << " (" << std::hex << current->product_id << std::dec << ")" << std::endl; - } - } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - for (size_t i = 0; i < _3DCONNEXION_VENDORS.size(); ++i) { if (_3DCONNEXION_VENDORS[i] == current->vendor_id) @@ -340,30 +361,8 @@ void Mouse3DController::run() void Mouse3DController::collect_input() { - auto convert_input = [](unsigned char first, unsigned char second)-> double - { - int ret = 0; - switch (second) - { - case 0: { ret = (int)first; break; } - case 1: { ret = (int)first + 255; break; } - case 254: { ret = -511 + (int)first; break; } - case 255: { ret = -255 + (int)first; break; } - default: { break; } - } - return (double)ret / 349.0; - }; - - // Read data from device - enum EDataType - { - Translation = 1, - Rotation, - Button - }; - - unsigned char retrieved_data[8] = { 0 }; - int res = hid_read_timeout(m_device, retrieved_data, sizeof(retrieved_data), 100); + DataPacket packet = { 0 }; + int res = hid_read_timeout(m_device, packet.data(), packet.size(), 100); if (res < 0) { // An error occourred (device detached from pc ?) @@ -371,73 +370,148 @@ void Mouse3DController::collect_input() return; } + if (!wxGetApp().IsActive()) + return; + std::lock_guard lock(m_mutex); - if (res > 0) + bool updated = false; + + if (res == 7) + updated = handle_packet(packet); + else if (res == 13) + updated = handle_wireless_packet(packet); + else if (res > 0) + std::cout << "Got unknown data packet of length: " << res << ", code:" << (int)packet[0] << std::endl; + + if (updated) + // ask for an idle event to update 3D scene + wxWakeUpIdle(); +} + +bool Mouse3DController::handle_packet(const DataPacket& packet) +{ + switch (packet[0]) { - bool updated = false; - - switch (retrieved_data[0]) + case 1: // Translation { - case Translation: - { - Vec3d translation(-convert_input(retrieved_data[1], retrieved_data[2]), - convert_input(retrieved_data[3], retrieved_data[4]), - convert_input(retrieved_data[5], retrieved_data[6])); - if (!translation.isApprox(Vec3d::Zero())) - { - updated = true; - m_state.append_translation(translation); - } + if (handle_packet_translation(packet)) + return true; - break; - } - case Rotation: - { - Vec3f rotation(-(float)convert_input(retrieved_data[1], retrieved_data[2]), - (float)convert_input(retrieved_data[3], retrieved_data[4]), - -(float)convert_input(retrieved_data[5], retrieved_data[6])); - if (!rotation.isApprox(Vec3f::Zero())) - { - updated = true; - m_state.append_rotation(rotation); - } - - break; - } - case Button: - { - // Because of lack of documentation, it is not clear how we should interpret the retrieved data for the button case. - // Experiments made with SpaceNavigator: - // retrieved_data[1] == 0 if no button pressed - // retrieved_data[1] == 1 if left button pressed - // retrieved_data[1] == 2 if right button pressed - // retrieved_data[1] == 3 if left and right button pressed - // seems to show that each button is associated to a bit of retrieved_data[1], which means that at max 8 buttons can be supported. - for (unsigned int i = 0; i < 8; ++i) - { - if (retrieved_data[1] & (0x1 << i)) - { - updated = true; - m_state.append_button(i); - } - } - -// // On the other hand, other libraries, as in https://github.com/koenieee/CrossplatformSpacemouseDriver/blob/master/SpaceMouseDriver/driver/SpaceMouseController.cpp -// // interpret retrieved_data[1] as the button id -// if (retrieved_data[1] > 0) -// m_state.set_button((unsigned int)retrieved_data[1]); - - break; - } - default: break; } + case 2: // Rotation + { + if (handle_packet_rotation(packet, 1)) + return true; - if (updated) - // ask for an idle event to update 3D scene - wxWakeUpIdle(); + break; + } + case 3: // Button + { + if (handle_packet_button(packet, 6)) + return true; + + break; + } + default: + { + std::cout << "Got unknown data packet of code: " << (int)packet[0] << std::endl; + break; + } } + + return false; +} + +bool Mouse3DController::handle_wireless_packet(const DataPacket& packet) +{ + switch (packet[0]) + { + case 1: // Translation + Rotation + { + bool updated = handle_packet_translation(packet); + updated |= handle_packet_rotation(packet, 7); + + if (updated) + return true; + + break; + } + case 3: // Button + { + if (handle_packet_button(packet, 12)) + return true; + + break; + } + default: + { + std::cout << "Got unknown data packet of code: " << (int)packet[0] << std::endl; + break; + } + } + + return false; +} + +double convert_input(unsigned char first, unsigned char second) +{ + int ret = 0; + switch (second) + { + case 0: { ret = (int)first; break; } + case 1: { ret = (int)first + 255; break; } + case 254: { ret = -511 + (int)first; break; } + case 255: { ret = -255 + (int)first; break; } + default: { break; } + } + return (double)ret / 349.0; +}; + +bool Mouse3DController::handle_packet_translation(const DataPacket& packet) +{ + Vec3d translation(-convert_input(packet[1], packet[2]), + convert_input(packet[3], packet[4]), + convert_input(packet[5], packet[6])); + if (!translation.isApprox(Vec3d::Zero())) + { + m_state.append_translation(translation); + return true; + } + + return false; +} + +bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigned int first_byte) +{ + Vec3f rotation(-(float)convert_input(packet[first_byte + 0], packet[first_byte + 1]), + (float)convert_input(packet[first_byte + 2], packet[first_byte + 3]), + -(float)convert_input(packet[first_byte + 4], packet[first_byte + 5])); + if (!rotation.isApprox(Vec3f::Zero())) + { + m_state.append_rotation(rotation); + return true; + } + + return false; +} + +bool Mouse3DController::handle_packet_button(const DataPacket& packet, unsigned int packet_size) +{ + for (unsigned int i = 0, base = 0; i < packet_size; ++i, base += 8) + { + for (unsigned int j = 0; j < 8; ++j) + { + if (packet[i + 1] & (0x1 << j)) + { + m_state.append_button(base + j); + return true; + } + } + } + + return false; } } // namespace GUI diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index a894e312b..d7f3ed9f7 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -104,6 +104,13 @@ private: // secondary thread methods void run(); void collect_input(); + + typedef std::array DataPacket; + bool handle_packet(const DataPacket& packet); + bool handle_wireless_packet(const DataPacket& packet); + bool handle_packet_translation(const DataPacket& packet); + bool handle_packet_rotation(const DataPacket& packet, unsigned int first_byte); + bool handle_packet_button(const DataPacket& packet, unsigned int packet_size); }; } // namespace GUI From 4ec6199ef103ff391d81c0a5c5278719eee4ac40 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 8 Oct 2019 14:32:05 +0200 Subject: [PATCH 17/73] ENABLE_3DCONNEXION_DEVICES -> Added translation and rotation customizable parameter deadzone --- src/slic3r/GUI/AppConfig.cpp | 42 +++++++++++++++-- src/slic3r/GUI/AppConfig.hpp | 8 ++-- src/slic3r/GUI/GLCanvas3D.cpp | 1 + src/slic3r/GUI/Mouse3DController.cpp | 68 ++++++++++++++++++++-------- src/slic3r/GUI/Mouse3DController.hpp | 31 ++++++++++--- 5 files changed, 116 insertions(+), 34 deletions(-) diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp index 6d0c83393..c29fe1c3b 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -272,7 +272,7 @@ void AppConfig::set_recent_projects(const std::vector& recent_proje } #if ENABLE_3DCONNEXION_DEVICES -void AppConfig::set_mouse_device(const std::string& name, double translation_speed, float rotation_speed) +void AppConfig::set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone) { std::string key = std::string("mouse_device:") + name; auto it = m_storage.find(key); @@ -281,10 +281,12 @@ void AppConfig::set_mouse_device(const std::string& name, double translation_spe it->second.clear(); it->second["translation_speed"] = std::to_string(translation_speed); + it->second["translation_deadzone"] = std::to_string(translation_deadzone); it->second["rotation_speed"] = std::to_string(rotation_speed); + it->second["rotation_deadzone"] = std::to_string(rotation_deadzone); } -bool AppConfig::get_mouse_device_translation_speed(const std::string& name, double& translation_speed) +bool AppConfig::get_mouse_device_translation_speed(const std::string& name, double& speed) { std::string key = std::string("mouse_device:") + name; auto it = m_storage.find(key); @@ -295,11 +297,26 @@ bool AppConfig::get_mouse_device_translation_speed(const std::string& name, doub if (it_val == it->second.end()) return false; - translation_speed = ::atof(it_val->second.c_str()); + speed = ::atof(it_val->second.c_str()); return true; } -bool AppConfig::get_mouse_device_rotation_speed(const std::string& name, float& rotation_speed) +bool AppConfig::get_mouse_device_translation_deadzone(const std::string& name, double& deadzone) +{ + std::string key = std::string("mouse_device:") + name; + auto it = m_storage.find(key); + if (it == m_storage.end()) + return false; + + auto it_val = it->second.find("translation_deadzone"); + if (it_val == it->second.end()) + return false; + + deadzone = ::atof(it_val->second.c_str()); + return true; +} + +bool AppConfig::get_mouse_device_rotation_speed(const std::string& name, float& speed) { std::string key = std::string("mouse_device:") + name; auto it = m_storage.find(key); @@ -310,7 +327,22 @@ bool AppConfig::get_mouse_device_rotation_speed(const std::string& name, float& if (it_val == it->second.end()) return false; - rotation_speed = (float)::atof(it_val->second.c_str()); + speed = (float)::atof(it_val->second.c_str()); + return true; +} + +bool AppConfig::get_mouse_device_rotation_deadzone(const std::string& name, float& deadzone) +{ + std::string key = std::string("mouse_device:") + name; + auto it = m_storage.find(key); + if (it == m_storage.end()) + return false; + + auto it_val = it->second.find("rotation_deadzone"); + if (it_val == it->second.end()) + return false; + + deadzone = (float)::atof(it_val->second.c_str()); return true; } #endif // ENABLE_3DCONNEXION_DEVICES diff --git a/src/slic3r/GUI/AppConfig.hpp b/src/slic3r/GUI/AppConfig.hpp index 1196404a2..fce0c0b39 100644 --- a/src/slic3r/GUI/AppConfig.hpp +++ b/src/slic3r/GUI/AppConfig.hpp @@ -132,9 +132,11 @@ public: void set_recent_projects(const std::vector& recent_projects); #if ENABLE_3DCONNEXION_DEVICES - void set_mouse_device(const std::string& name, double translation_speed, float rotation_speed); - bool get_mouse_device_translation_speed(const std::string& name, double& translation_speed); - bool get_mouse_device_rotation_speed(const std::string& name, float& rotation_speed); + void set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone); + bool get_mouse_device_translation_speed(const std::string& name, double& speed); + bool get_mouse_device_translation_deadzone(const std::string& name, double& deadzone); + bool get_mouse_device_rotation_speed(const std::string& name, float& speed); + bool get_mouse_device_rotation_deadzone(const std::string& name, float& deadzone); #endif // ENABLE_3DCONNEXION_DEVICES static const std::string SECTION_FILAMENTS; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f80e0d0dd..5eca428b0 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2399,6 +2399,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) { Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller(); controller.show_settings_dialog(!controller.is_settings_dialog_shown()); + m_dirty = true; break; } #endif // ENABLE_3DCONNEXION_DEVICES diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index c62b8c851..53bb9faa8 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -52,11 +52,15 @@ namespace Slic3r { namespace GUI { const double Mouse3DController::State::DefaultTranslationScale = 2.5; +const double Mouse3DController::State::MaxTranslationDeadzone = 0.1; +const double Mouse3DController::State::DefaultTranslationDeadzone = 0.5 * Mouse3DController::State::MaxTranslationDeadzone; const float Mouse3DController::State::DefaultRotationScale = 1.0f; +const float Mouse3DController::State::MaxRotationDeadzone = 0.1f; +const float Mouse3DController::State::DefaultRotationDeadzone = 0.5f * Mouse3DController::State::MaxRotationDeadzone; Mouse3DController::State::State() - : m_translation_scale(DefaultTranslationScale) - , m_rotation_scale(DefaultRotationScale) + : m_translation_params(DefaultTranslationScale, DefaultTranslationDeadzone) + , m_rotation_params(DefaultRotationScale, DefaultRotationDeadzone) , m_mouse_wheel_counter(0) { } @@ -87,7 +91,7 @@ bool Mouse3DController::State::apply(Camera& camera) if (has_translation()) { const Vec3d& translation = m_translation.front(); - camera.set_target(camera.get_target() + m_translation_scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up())); + camera.set_target(camera.get_target() + m_translation_params.scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up())); m_translation.pop(); ret = true; } @@ -95,8 +99,8 @@ bool Mouse3DController::State::apply(Camera& camera) if (has_rotation()) { const Vec3f& rotation = m_rotation.front(); - float theta = m_rotation_scale * rotation(0); - float phi = m_rotation_scale * rotation(2); + float theta = m_rotation_params.scale * rotation(0); + float phi = m_rotation_params.scale * rotation(2); float sign = camera.inverted_phi ? -1.0f : 1.0f; camera.phi += sign * phi; camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); @@ -207,13 +211,26 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign imgui.text(_(L("Speed:"))); ImGui::PopStyleColor(); - float translation = (float)m_state.get_translation_scale() / State::DefaultTranslationScale; - if (ImGui::SliderFloat(_(L("Translation")), &translation, 0.5f, 2.0f, "%.1f")) - m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation); + float translation_scale = (float)m_state.get_translation_scale() / State::DefaultTranslationScale; + if (ImGui::SliderFloat(_(L("Translation##1")), &translation_scale, 0.5f, 2.0f, "%.1f")) + m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation_scale); - float rotation = m_state.get_rotation_scale() / State::DefaultRotationScale; - if (ImGui::SliderFloat(_(L("Rotation")), &rotation, 0.5f, 2.0f, "%.1f")) - m_state.set_rotation_scale(State::DefaultRotationScale * rotation); + float rotation_scale = m_state.get_rotation_scale() / State::DefaultRotationScale; + if (ImGui::SliderFloat(_(L("Rotation##1")), &rotation_scale, 0.5f, 2.0f, "%.1f")) + m_state.set_rotation_scale(State::DefaultRotationScale * rotation_scale); + + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text(_(L("Deadzone:"))); + ImGui::PopStyleColor(); + + float translation_deadzone = (float)m_state.get_translation_deadzone(); + if (ImGui::SliderFloat(_(L("Translation##2")), &translation_deadzone, 0.0f, (float)State::MaxTranslationDeadzone, "%.2f")) + m_state.set_translation_deadzone((double)translation_deadzone); + + float rotation_deadzone = m_state.get_rotation_deadzone(); + if (ImGui::SliderFloat(_(L("Rotation##2")), &rotation_deadzone, 0.0f, State::MaxRotationDeadzone, "%.2f")) + m_state.set_rotation_deadzone(rotation_deadzone); imgui.end(); @@ -308,13 +325,19 @@ bool Mouse3DController::connect_device() BOOST_LOG_TRIVIAL(info) << "Connected device: " << m_device_str; // get device parameters from the config, if present - double translation = 1.0; - float rotation = 1.0; - wxGetApp().app_config->get_mouse_device_translation_speed(m_device_str, translation); - wxGetApp().app_config->get_mouse_device_rotation_speed(m_device_str, rotation); + double translation_speed = 1.0; + float rotation_speed = 1.0; + double translation_deadzone = State::DefaultTranslationDeadzone; + float rotation_deadzone = State::DefaultRotationDeadzone; + wxGetApp().app_config->get_mouse_device_translation_speed(m_device_str, translation_speed); + wxGetApp().app_config->get_mouse_device_translation_deadzone(m_device_str, translation_deadzone); + wxGetApp().app_config->get_mouse_device_rotation_speed(m_device_str, rotation_speed); + wxGetApp().app_config->get_mouse_device_rotation_deadzone(m_device_str, rotation_deadzone); // clamp to valid values - m_state.set_translation_scale(State::DefaultTranslationScale * std::max(0.5, std::min(2.0, translation))); - m_state.set_rotation_scale(State::DefaultRotationScale * std::max(0.5f, std::min(2.0f, rotation))); + m_state.set_translation_scale(State::DefaultTranslationScale* std::max(0.5, std::min(2.0, translation_speed))); + m_state.set_translation_deadzone(std::max(0.0, std::min(State::MaxTranslationDeadzone, translation_deadzone))); + m_state.set_rotation_scale(State::DefaultRotationScale* std::max(0.5f, std::min(2.0f, rotation_speed))); + m_state.set_rotation_deadzone(std::max(0.0f, std::min(State::MaxRotationDeadzone, rotation_deadzone))); } return (m_device != nullptr); @@ -330,7 +353,8 @@ void Mouse3DController::disconnect_device() m_thread.join(); // Store current device parameters into the config - wxGetApp().app_config->set_mouse_device(m_device_str, m_state.get_translation_scale() / State::DefaultTranslationScale, m_state.get_rotation_scale() / State::DefaultRotationScale); + wxGetApp().app_config->set_mouse_device(m_device_str, m_state.get_translation_scale() / State::DefaultTranslationScale, m_state.get_translation_deadzone(), + m_state.get_rotation_scale() / State::DefaultRotationScale, m_state.get_rotation_deadzone()); wxGetApp().app_config->save(); // Close the 3Dconnexion device @@ -474,7 +498,9 @@ bool Mouse3DController::handle_packet_translation(const DataPacket& packet) Vec3d translation(-convert_input(packet[1], packet[2]), convert_input(packet[3], packet[4]), convert_input(packet[5], packet[6])); - if (!translation.isApprox(Vec3d::Zero())) + + double deadzone = m_state.get_translation_deadzone(); + if ((std::abs(translation(0)) > deadzone) || (std::abs(translation(1)) > deadzone) || (std::abs(translation(2)) > deadzone)) { m_state.append_translation(translation); return true; @@ -488,7 +514,9 @@ bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigne Vec3f rotation(-(float)convert_input(packet[first_byte + 0], packet[first_byte + 1]), (float)convert_input(packet[first_byte + 2], packet[first_byte + 3]), -(float)convert_input(packet[first_byte + 4], packet[first_byte + 5])); - if (!rotation.isApprox(Vec3f::Zero())) + + float deadzone = m_state.get_rotation_deadzone(); + if ((std::abs(rotation(0)) > deadzone) || (std::abs(rotation(1)) > deadzone) || (std::abs(rotation(2)) > deadzone)) { m_state.append_rotation(rotation); return true; diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index d7f3ed9f7..33295a68a 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -22,15 +22,28 @@ class Mouse3DController { public: static const double DefaultTranslationScale; + static const double MaxTranslationDeadzone; + static const double DefaultTranslationDeadzone; static const float DefaultRotationScale; + static const float MaxRotationDeadzone; + static const float DefaultRotationDeadzone; private: + template + struct CustomParameters + { + Number scale; + Number deadzone; + + CustomParameters(Number scale, Number deadzone) : scale(scale), deadzone(deadzone) {} + }; + std::queue m_translation; std::queue m_rotation; std::queue m_buttons; - double m_translation_scale; - float m_rotation_scale; + CustomParameters m_translation_params; + CustomParameters m_rotation_params; // When the 3Dconnexion driver is running the system gets, by default, mouse wheel events when rotations around the X axis are detected. // We want to filter these out because we are getting the data directly from the device, bypassing the driver, and those mouse wheel events interfere @@ -59,11 +72,17 @@ class Mouse3DController bool process_mouse_wheel(); - double get_translation_scale() const { return m_translation_scale; } - void set_translation_scale(double scale) { m_translation_scale = scale; } + double get_translation_scale() const { return m_translation_params.scale; } + void set_translation_scale(double scale) { m_translation_params.scale = scale; } - float get_rotation_scale() const { return m_rotation_scale; } - void set_rotation_scale(float scale) { m_rotation_scale = scale; } + float get_rotation_scale() const { return m_rotation_params.scale; } + void set_rotation_scale(float scale) { m_rotation_params.scale = scale; } + + double get_translation_deadzone() const { return m_translation_params.deadzone; } + void set_translation_deadzone(double deadzone) { m_translation_params.deadzone = deadzone; } + + float get_rotation_deadzone() const { return m_rotation_params.deadzone; } + void set_rotation_deadzone(float deadzone) { m_rotation_params.deadzone = deadzone; } // return true if any change to the camera took place bool apply(Camera& camera); From b41a0656b99860b3b6e1f410bb0ca57fe2c0fa59 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 9 Oct 2019 14:01:13 +0200 Subject: [PATCH 18/73] ENABLE_3DCONNEXION_DEVICES -> Reworked parsing of data coming from device --- src/slic3r/GUI/Mouse3DController.cpp | 57 ++++++++-------------------- 1 file changed, 15 insertions(+), 42 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 53bb9faa8..86f6fa5a7 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -14,6 +14,8 @@ #include #include "I18N.hpp" +#include + // WARN: If updating these lists, please also update resources/udev/90-3dconnexion.rules static const std::vector _3DCONNEXION_VENDORS = @@ -36,16 +38,16 @@ static const std::vector _3DCONNEXION_DEVICES = 0xc628, /* 50728 space navigator for notebooks*/ 0xc629, /* 50729 space pilot pro*/ 0xc62b, /* 50731 space mouse pro*/ - 0xc62e, /* 50734 spacemouse wireless (USB cable) */ - 0xc62f, /* 50735 spacemouse wireless receiver */ + 0xc62e, /* 50734 spacemouse wireless (USB cable) *TESTED* */ + 0xc62f, /* 50735 spacemouse wireless receiver */ 0xc631, /* 50737 spacemouse pro wireless *TESTED* */ 0xc632, /* 50738 spacemouse pro wireless receiver */ 0xc633, /* 50739 spacemouse enterprise */ - 0xc635, /* 50741 spacemouse compact */ + 0xc635, /* 50741 spacemouse compact *TESTED* */ 0xc636, /* 50742 spacemouse module */ 0xc640, /* 50752 nulooq */ -// 0xc652, /* 50770 3Dconnexion universal receiver */ +// 0xc652, /* 50770 3Dconnexion universal receiver */ }; namespace Slic3r { @@ -254,26 +256,6 @@ bool Mouse3DController::connect_device() unsigned short vendor_id = 0; unsigned short product_id = 0; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - hid_device_info* cur = devices; - while (cur != nullptr) - { - std::cout << "Detected device '"; - - if (cur->manufacturer_string != nullptr) - std::wcout << cur->manufacturer_string << L" "; - else - std::wcout << "MMM "; - if (cur->product_string != nullptr) - std::wcout << cur->product_string; - else - std::wcout << "PPP"; - std::cout << "' code: " << cur->product_id << " (" << std::hex << cur->product_id << std::dec << ")" << std::endl; - - cur = cur->next; - } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - hid_device_info* current = devices; while (current != nullptr) { @@ -481,17 +463,9 @@ bool Mouse3DController::handle_wireless_packet(const DataPacket& packet) double convert_input(unsigned char first, unsigned char second) { - int ret = 0; - switch (second) - { - case 0: { ret = (int)first; break; } - case 1: { ret = (int)first + 255; break; } - case 254: { ret = -511 + (int)first; break; } - case 255: { ret = -255 + (int)first; break; } - default: { break; } - } - return (double)ret / 349.0; -}; + short value = first | second << 8; + return (double)value / 350.0; +} bool Mouse3DController::handle_packet_translation(const DataPacket& packet) { @@ -527,15 +501,14 @@ bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigne bool Mouse3DController::handle_packet_button(const DataPacket& packet, unsigned int packet_size) { - for (unsigned int i = 0, base = 0; i < packet_size; ++i, base += 8) + unsigned int data = packet[1] | packet[2] << 8 | packet[3] << 16 | packet[4] << 24; + const std::bitset<32> data_bits{ data }; + for (size_t i = 0; i < data_bits.size(); ++i) { - for (unsigned int j = 0; j < 8; ++j) + if (data_bits.test(i)) { - if (packet[i + 1] & (0x1 << j)) - { - m_state.append_button(base + j); - return true; - } + m_state.append_button((unsigned int)i); + return true; } } From a735ec1b48c159403637dcd202a4ca4d98278821 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 9 Oct 2019 14:18:43 +0200 Subject: [PATCH 19/73] ENABLE_3DCONNEXION_DEVICES -> Added tech ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT for debug output --- src/libslic3r/Technologies.hpp | 2 ++ src/slic3r/GUI/Mouse3DController.cpp | 28 +++++++++++++++++++++++++++- src/slic3r/GUI/Mouse3DController.hpp | 8 +++++++- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index da982c49a..f64bea172 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -39,6 +39,8 @@ // Enabled 3Dconnexion devices #define ENABLE_3DCONNEXION_DEVICES (1 && ENABLE_2_0_0_ALPHA1) +// Enabled 3Dconnexion devices debug output +#define ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT (1 && ENABLE_3DCONNEXION_DEVICES) #endif // _technologies_h_ diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 86f6fa5a7..00d5e184a 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -111,7 +111,7 @@ bool Mouse3DController::State::apply(Camera& camera) ret = true; } - if (has_any_button()) + if (has_button()) { unsigned int button = m_buttons.front(); switch (button) @@ -234,6 +234,18 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign if (ImGui::SliderFloat(_(L("Rotation##2")), &rotation_deadzone, 0.0f, State::MaxRotationDeadzone, "%.2f")) m_state.set_rotation_deadzone(rotation_deadzone); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text(_(L("DEBUG:"))); + ImGui::PopStyleColor(); + Vec3f translation = m_state.get_translation().cast(); + Vec3f rotation = m_state.get_rotation(); + unsigned int button = m_state.get_button(); + ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); + ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + imgui.end(); ImGui::PopStyleVar(); @@ -256,6 +268,20 @@ bool Mouse3DController::connect_device() unsigned short vendor_id = 0; unsigned short product_id = 0; +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + hid_device_info* cur = devices; + while (cur != nullptr) + { + std::cout << "Detected device '"; + std::wcout << ((cur->manufacturer_string != nullptr) ? cur->manufacturer_string : L"Unknown"); + std::cout << "::"; + std::wcout << ((cur->product_string != nullptr) ? cur->product_string : L"Unknown"); + std::cout << "' code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")" << std::endl; + + cur = cur->next; + } +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + hid_device_info* current = devices; while (current != nullptr) { diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 33295a68a..3136f9396 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -68,7 +68,13 @@ class Mouse3DController bool has_translation() const { return !m_translation.empty(); } bool has_rotation() const { return !m_rotation.empty(); } - bool has_any_button() const { return !m_buttons.empty(); } + bool has_button() const { return !m_buttons.empty(); } + +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + Vec3d get_translation() const { return has_translation() ? m_translation.front() : Vec3d::Zero(); } + Vec3f get_rotation() const { return has_rotation() ? m_rotation.front() : Vec3f::Zero(); } + unsigned int get_button() const { return has_button() ? m_buttons.front() : 0; } +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT bool process_mouse_wheel(); From 58884774737cd1dff406d4226d618ec2acf7fb40 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 9 Oct 2019 14:39:28 +0200 Subject: [PATCH 20/73] ENABLE_3DCONNEXION_DEVICES -> Reworked deadzone check --- src/slic3r/GUI/Mouse3DController.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 00d5e184a..0462e21e2 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -487,20 +487,21 @@ bool Mouse3DController::handle_wireless_packet(const DataPacket& packet) return false; } -double convert_input(unsigned char first, unsigned char second) +double convert_input(unsigned char first, unsigned char second, double deadzone) { short value = first | second << 8; - return (double)value / 350.0; + double ret = (double)value / 350.0; + return (std::abs(ret) > deadzone) ? ret : 0.0; } bool Mouse3DController::handle_packet_translation(const DataPacket& packet) { - Vec3d translation(-convert_input(packet[1], packet[2]), - convert_input(packet[3], packet[4]), - convert_input(packet[5], packet[6])); - double deadzone = m_state.get_translation_deadzone(); - if ((std::abs(translation(0)) > deadzone) || (std::abs(translation(1)) > deadzone) || (std::abs(translation(2)) > deadzone)) + Vec3d translation(-convert_input(packet[1], packet[2], deadzone), + convert_input(packet[3], packet[4], deadzone), + convert_input(packet[5], packet[6], deadzone)); + + if (!translation.isApprox(Vec3d::Zero())) { m_state.append_translation(translation); return true; @@ -511,12 +512,12 @@ bool Mouse3DController::handle_packet_translation(const DataPacket& packet) bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigned int first_byte) { - Vec3f rotation(-(float)convert_input(packet[first_byte + 0], packet[first_byte + 1]), - (float)convert_input(packet[first_byte + 2], packet[first_byte + 3]), - -(float)convert_input(packet[first_byte + 4], packet[first_byte + 5])); + double deadzone = (double)m_state.get_rotation_deadzone(); + Vec3f rotation(-(float)convert_input(packet[first_byte + 0], packet[first_byte + 1], deadzone), + (float)convert_input(packet[first_byte + 2], packet[first_byte + 3], deadzone), + -(float)convert_input(packet[first_byte + 4], packet[first_byte + 5], deadzone)); - float deadzone = m_state.get_rotation_deadzone(); - if ((std::abs(rotation(0)) > deadzone) || (std::abs(rotation(1)) > deadzone) || (std::abs(rotation(2)) > deadzone)) + if (!rotation.isApprox(Vec3f::Zero())) { m_state.append_rotation(rotation); return true; From 3a5823bc2eda0ef7ce1619e4fe852ee2b926247d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 9 Oct 2019 15:23:30 +0200 Subject: [PATCH 21/73] ENABLE_3DCONNEXION_DEVICES -> Added temporary debug output to measure input queue sizes --- src/slic3r/GUI/Mouse3DController.cpp | 45 +++++++++++++++++++++++- src/slic3r/GUI/Mouse3DController.hpp | 52 ++++++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 0462e21e2..e22476671 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -64,6 +64,13 @@ Mouse3DController::State::State() : m_translation_params(DefaultTranslationScale, DefaultTranslationDeadzone) , m_rotation_params(DefaultRotationScale, DefaultRotationDeadzone) , m_mouse_wheel_counter(0) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + , m_translation_queue_max_size(0) + , m_rotation_queue_max_size(0) + , m_buttons_queue_max_size(0) +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { } @@ -235,15 +242,51 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign m_state.set_rotation_deadzone(rotation_deadzone); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + ImGui::Separator(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ImGui::Separator(); ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text(_(L("DEBUG:"))); + imgui.text("DEBUG:"); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + imgui.text("Vectors:"); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ImGui::PopStyleColor(); Vec3f translation = m_state.get_translation().cast(); Vec3f rotation = m_state.get_rotation(); unsigned int button = m_state.get_button(); ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text("Queue size:"); + ImGui::PopStyleColor(); + + int translation_size[2] = { (int)m_state.get_translation_queue_size(), (int)m_state.get_translation_queue_max_size() }; + int rotation_size[2] = { (int)m_state.get_rotation_queue_size(), (int)m_state.get_rotation_queue_max_size() }; + int buttons_size[2] = { (int)m_state.get_buttons_queue_size(), (int)m_state.get_buttons_queue_max_size() }; + + ImGui::InputInt2("Translation##4", translation_size, ImGuiInputTextFlags_ReadOnly); + ImGui::InputInt2("Rotation##4", rotation_size, ImGuiInputTextFlags_ReadOnly); + ImGui::InputInt2("Buttons", buttons_size, ImGuiInputTextFlags_ReadOnly); + +/* + int translation_size = (int)m_state.get_translation_queue_size(); + int translation_max_size = (int)m_state.get_translation_queue_max_size(); + int rotation_size = (int)m_state.get_rotation_queue_size(); + int rotation_max_size = (int)m_state.get_rotation_queue_max_size(); + int buttons_size = (int)m_state.get_buttons_queue_size(); + int buttons_max_size = (int)m_state.get_buttons_queue_max_size(); + + ImGui::InputInt("Translation size", &translation_size, 0, 0, ImGuiInputTextFlags_ReadOnly); + ImGui::InputInt("Translation max", &translation_max_size, 0, 0, ImGuiInputTextFlags_ReadOnly); + ImGui::InputInt("Rotation size", &rotation_size, 0, 0, ImGuiInputTextFlags_ReadOnly); + ImGui::InputInt("Rotation max", &rotation_max_size, 0, 0, ImGuiInputTextFlags_ReadOnly); + ImGui::InputInt("Button size", &buttons_size, 0, 0, ImGuiInputTextFlags_ReadOnly); + ImGui::InputInt("Button max", &buttons_max_size, 0, 0, ImGuiInputTextFlags_ReadOnly); +*/ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT imgui.end(); diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 3136f9396..2f3514048 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -54,17 +54,53 @@ class Mouse3DController // GLCanvas3D::on_idle() through the call to the apply() method unsigned int m_mouse_wheel_counter; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + unsigned int m_translation_queue_max_size; + unsigned int m_rotation_queue_max_size; + unsigned int m_buttons_queue_max_size; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + public: State(); - void append_translation(const Vec3d& translation) { m_translation.push(translation); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void append_translation(const Vec3d& translation) + { + m_translation.push(translation); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_translation_queue_max_size = std::max(m_translation_queue_max_size, (unsigned int)m_translation.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + } + void append_rotation(const Vec3f& rotation) { m_rotation.push(rotation); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_rotation_queue_max_size = std::max(m_rotation_queue_max_size, (unsigned int)m_rotation.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT if (rotation(0) != 0.0f) ++m_mouse_wheel_counter; } - void append_button(unsigned int id) { m_buttons.push(id); } + + void append_button(unsigned int id) + { + m_buttons.push(id); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, (unsigned int)m_buttons.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + } + +// void append_translation(const Vec3d& translation) { m_translation.push(translation); } +// void append_rotation(const Vec3f& rotation) +// { +// m_rotation.push(rotation); +// if (rotation(0) != 0.0f) +// ++m_mouse_wheel_counter; +// } +// void append_button(unsigned int id) { m_buttons.push(id); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool has_translation() const { return !m_translation.empty(); } bool has_rotation() const { return !m_rotation.empty(); } @@ -90,6 +126,18 @@ class Mouse3DController float get_rotation_deadzone() const { return m_rotation_params.deadzone; } void set_rotation_deadzone(float deadzone) { m_rotation_params.deadzone = deadzone; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + unsigned int get_translation_queue_size() const { return (unsigned int)m_translation.size(); } + unsigned int get_rotation_queue_size() const { return (unsigned int)m_rotation.size(); } + unsigned int get_buttons_queue_size() const { return (unsigned int)m_buttons.size(); } + + unsigned int get_translation_queue_max_size() const { return m_translation_queue_max_size; } + unsigned int get_rotation_queue_max_size() const { return m_rotation_queue_max_size; } + unsigned int get_buttons_queue_max_size() const { return m_buttons_queue_max_size; } +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + // return true if any change to the camera took place bool apply(Camera& camera); }; From aae1250921491814dffe4fd340ca293d6d23284b Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 10 Oct 2019 09:04:44 +0200 Subject: [PATCH 22/73] ENABLE_3DCONNEXION_DEVICES -> Experimental input queues with max size --- src/slic3r/GUI/Mouse3DController.cpp | 106 ++++++++++++++++++++----- src/slic3r/GUI/Mouse3DController.hpp | 111 ++++++++++++++++++++++++--- 2 files changed, 190 insertions(+), 27 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index e22476671..10f365dd7 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -54,10 +54,10 @@ namespace Slic3r { namespace GUI { const double Mouse3DController::State::DefaultTranslationScale = 2.5; -const double Mouse3DController::State::MaxTranslationDeadzone = 0.1; +const double Mouse3DController::State::MaxTranslationDeadzone = 0.2; const double Mouse3DController::State::DefaultTranslationDeadzone = 0.5 * Mouse3DController::State::MaxTranslationDeadzone; const float Mouse3DController::State::DefaultRotationScale = 1.0f; -const float Mouse3DController::State::MaxRotationDeadzone = 0.1f; +const float Mouse3DController::State::MaxRotationDeadzone = (float)Mouse3DController::State::MaxTranslationDeadzone; const float Mouse3DController::State::DefaultRotationDeadzone = 0.5f * Mouse3DController::State::MaxRotationDeadzone; Mouse3DController::State::State() @@ -78,7 +78,15 @@ bool Mouse3DController::State::process_mouse_wheel() { if (m_mouse_wheel_counter == 0) return false; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_QUEUE_MAX_SIZE + else if (!m_rotation.queue.empty()) +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ else if (!m_rotation.empty()) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_QUEUE_MAX_SIZE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { --m_mouse_wheel_counter; return true; @@ -99,14 +107,35 @@ bool Mouse3DController::State::apply(Camera& camera) if (has_translation()) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_QUEUE_MAX_SIZE + const Vec3d& translation = m_translation.queue.front(); + camera.set_target(camera.get_target() + m_translation_params.scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up())); + m_translation.queue.pop(); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ const Vec3d& translation = m_translation.front(); camera.set_target(camera.get_target() + m_translation_params.scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up())); m_translation.pop(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_QUEUE_MAX_SIZE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ret = true; } if (has_rotation()) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_QUEUE_MAX_SIZE + const Vec3f& rotation = m_rotation.queue.front(); + float theta = m_rotation_params.scale * rotation(0); + float phi = m_rotation_params.scale * rotation(2); + float sign = camera.inverted_phi ? -1.0f : 1.0f; + camera.phi += sign * phi; + camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); + m_rotation.queue.pop(); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ const Vec3f& rotation = m_rotation.front(); float theta = m_rotation_params.scale * rotation(0); float phi = m_rotation_params.scale * rotation(2); @@ -114,12 +143,27 @@ bool Mouse3DController::State::apply(Camera& camera) camera.phi += sign * phi; camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); m_rotation.pop(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_QUEUE_MAX_SIZE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ret = true; } if (has_button()) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_QUEUE_MAX_SIZE + unsigned int button = m_buttons.queue.front(); + switch (button) + { + case 0: { camera.update_zoom(1.0); break; } + case 1: { camera.update_zoom(-1.0); break; } + default: { break; } + } + m_buttons.queue.pop(); +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ unsigned int button = m_buttons.front(); switch (button) { @@ -128,6 +172,9 @@ bool Mouse3DController::State::apply(Camera& camera) default: { break; } } m_buttons.pop(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_QUEUE_MAX_SIZE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ret = true; } @@ -271,21 +318,14 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign ImGui::InputInt2("Rotation##4", rotation_size, ImGuiInputTextFlags_ReadOnly); ImGui::InputInt2("Buttons", buttons_size, ImGuiInputTextFlags_ReadOnly); -/* - int translation_size = (int)m_state.get_translation_queue_size(); - int translation_max_size = (int)m_state.get_translation_queue_max_size(); - int rotation_size = (int)m_state.get_rotation_queue_size(); - int rotation_max_size = (int)m_state.get_rotation_queue_max_size(); - int buttons_size = (int)m_state.get_buttons_queue_size(); - int buttons_max_size = (int)m_state.get_buttons_queue_max_size(); - - ImGui::InputInt("Translation size", &translation_size, 0, 0, ImGuiInputTextFlags_ReadOnly); - ImGui::InputInt("Translation max", &translation_max_size, 0, 0, ImGuiInputTextFlags_ReadOnly); - ImGui::InputInt("Rotation size", &rotation_size, 0, 0, ImGuiInputTextFlags_ReadOnly); - ImGui::InputInt("Rotation max", &rotation_max_size, 0, 0, ImGuiInputTextFlags_ReadOnly); - ImGui::InputInt("Button size", &buttons_size, 0, 0, ImGuiInputTextFlags_ReadOnly); - ImGui::InputInt("Button max", &buttons_max_size, 0, 0, ImGuiInputTextFlags_ReadOnly); -*/ +#if ENABLE_QUEUE_MAX_SIZE + int queue_size = (int)m_state.get_queues_max_size(); + if (ImGui::InputInt("Max size", &queue_size, 1, 1, ImGuiInputTextFlags_ReadOnly)) + { + if (queue_size > 0) + m_state.set_queues_max_size(queue_size); + } +#endif // ENABLE_QUEUE_MAX_SIZE //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT @@ -312,6 +352,10 @@ bool Mouse3DController::connect_device() unsigned short product_id = 0; #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + unsigned short usage_page = 0; + unsigned short usage = 0; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ hid_device_info* cur = devices; while (cur != nullptr) { @@ -344,6 +388,12 @@ bool Mouse3DController::connect_device() if (_3DCONNEXION_DEVICES[i] == current->product_id) { product_id = current->product_id; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + usage_page = current->usage_page; + usage = current->usage; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ break; } } @@ -369,11 +419,31 @@ bool Mouse3DController::connect_device() if (m_device != nullptr) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + std::vector manufacturer(1024, 0); + hid_get_manufacturer_string(m_device, manufacturer.data(), 1024); + m_device_str = boost::nowide::narrow(manufacturer.data()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + std::vector product(1024, 0); hid_get_product_string(m_device, product.data(), 1024); - m_device_str = boost::nowide::narrow(product.data()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + m_device_str += "/" + boost::nowide::narrow(product.data()); +// m_device_str = boost::nowide::narrow(product.data()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ BOOST_LOG_TRIVIAL(info) << "Connected device: " << m_device_str; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << std::endl << "Connected device:" << std::endl; + std::cout << "Manufacturer/product..........: " << m_device_str << std::endl; + std::cout << "Manufacturer id/product id....: " << vendor_id << "/" << product_id << std::endl; + std::cout << "Manufacturer id/product id hex: " << std::hex << vendor_id << "/" << product_id << std::dec << std::endl; + std::cout << "Usage page....................: " << usage_page << std::endl; + std::cout << "Usage.........................: " << usage << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + // get device parameters from the config, if present double translation_speed = 1.0; diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 2f3514048..c06f0863e 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -11,6 +11,10 @@ #include #include +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#define ENABLE_QUEUE_MAX_SIZE 1 +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + namespace Slic3r { namespace GUI { @@ -38,9 +42,27 @@ class Mouse3DController CustomParameters(Number scale, Number deadzone) : scale(scale), deadzone(deadzone) {} }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_QUEUE_MAX_SIZE + template + struct InputQueue + { + size_t max_size{ 20 }; + std::queue queue; + }; + + InputQueue m_translation; + InputQueue m_rotation; + InputQueue m_buttons; +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::queue m_translation; std::queue m_rotation; std::queue m_buttons; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_QUEUE_MAX_SIZE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + CustomParameters m_translation_params; CustomParameters m_rotation_params; @@ -68,48 +90,92 @@ class Mouse3DController //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void append_translation(const Vec3d& translation) { +#if ENABLE_QUEUE_MAX_SIZE + while (m_translation.queue.size() >= m_translation.max_size) + { + m_translation.queue.pop(); + } + m_translation.queue.push(translation); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_translation_queue_max_size = std::max(m_translation_queue_max_size, (unsigned int)m_translation.queue.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +#else m_translation.push(translation); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT m_translation_queue_max_size = std::max(m_translation_queue_max_size, (unsigned int)m_translation.size()); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +#endif // ENABLE_QUEUE_MAX_SIZE } void append_rotation(const Vec3f& rotation) { +#if ENABLE_QUEUE_MAX_SIZE + while (m_rotation.queue.size() >= m_rotation.max_size) + { + m_rotation.queue.pop(); + } + m_rotation.queue.push(rotation); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_rotation_queue_max_size = std::max(m_rotation_queue_max_size, (unsigned int)m_rotation.queue.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + if (rotation(0) != 0.0f) + ++m_mouse_wheel_counter; +#else m_rotation.push(rotation); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT m_rotation_queue_max_size = std::max(m_rotation_queue_max_size, (unsigned int)m_rotation.size()); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT if (rotation(0) != 0.0f) ++m_mouse_wheel_counter; +#endif // ENABLE_QUEUE_MAX_SIZE } void append_button(unsigned int id) { +#if ENABLE_QUEUE_MAX_SIZE + while (m_buttons.queue.size() >= m_buttons.max_size) + { + m_buttons.queue.pop(); + } + m_buttons.queue.push(id); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, (unsigned int)m_buttons.queue.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +#else m_buttons.push(id); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, (unsigned int)m_buttons.size()); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +#endif // ENABLE_QUEUE_MAX_SIZE } - -// void append_translation(const Vec3d& translation) { m_translation.push(translation); } -// void append_rotation(const Vec3f& rotation) -// { -// m_rotation.push(rotation); -// if (rotation(0) != 0.0f) -// ++m_mouse_wheel_counter; -// } -// void append_button(unsigned int id) { m_buttons.push(id); } //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_QUEUE_MAX_SIZE + bool has_translation() const { return !m_translation.queue.empty(); } + bool has_rotation() const { return !m_rotation.queue.empty(); } + bool has_button() const { return !m_buttons.queue.empty(); } +#else bool has_translation() const { return !m_translation.empty(); } bool has_rotation() const { return !m_rotation.empty(); } bool has_button() const { return !m_buttons.empty(); } +#endif // ENABLE_QUEUE_MAX_SIZE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_QUEUE_MAX_SIZE + Vec3d get_translation() const { return has_translation() ? m_translation.queue.front() : Vec3d::Zero(); } + Vec3f get_rotation() const { return has_rotation() ? m_rotation.queue.front() : Vec3f::Zero(); } + unsigned int get_button() const { return has_button() ? m_buttons.queue.front() : 0; } +#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Vec3d get_translation() const { return has_translation() ? m_translation.front() : Vec3d::Zero(); } Vec3f get_rotation() const { return has_rotation() ? m_rotation.front() : Vec3f::Zero(); } unsigned int get_button() const { return has_button() ? m_buttons.front() : 0; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_QUEUE_MAX_SIZE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT bool process_mouse_wheel(); @@ -128,14 +194,41 @@ class Mouse3DController //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +#if ENABLE_QUEUE_MAX_SIZE + unsigned int get_translation_queue_size() const { return (unsigned int)m_translation.queue.size(); } + unsigned int get_rotation_queue_size() const { return (unsigned int)m_rotation.queue.size(); } + unsigned int get_buttons_queue_size() const { return (unsigned int)m_buttons.queue.size(); } +#else unsigned int get_translation_queue_size() const { return (unsigned int)m_translation.size(); } unsigned int get_rotation_queue_size() const { return (unsigned int)m_rotation.size(); } unsigned int get_buttons_queue_size() const { return (unsigned int)m_buttons.size(); } +#endif // ENABLE_QUEUE_MAX_SIZE unsigned int get_translation_queue_max_size() const { return m_translation_queue_max_size; } unsigned int get_rotation_queue_max_size() const { return m_rotation_queue_max_size; } unsigned int get_buttons_queue_max_size() const { return m_buttons_queue_max_size; } #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_QUEUE_MAX_SIZE + size_t get_queues_max_size() const { return m_translation.max_size; } + void set_queues_max_size(size_t size) + { + if (size > 0) + { + std::cout << "New queues max size: " << size << std::endl; + + m_translation.max_size = size; + m_rotation.max_size = size; + m_buttons.max_size = size; + + m_translation_queue_max_size = 0; + m_rotation_queue_max_size = 0; + m_buttons_queue_max_size = 0; + } + } +#endif // ENABLE_QUEUE_MAX_SIZE //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // return true if any change to the camera took place From ded6d443899945c14c5206e65c0858950c00fd81 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 10 Oct 2019 09:29:55 +0200 Subject: [PATCH 23/73] Fixed build on OsX --- src/slic3r/GUI/Mouse3DController.cpp | 1 - src/slic3r/GUI/Mouse3DController.hpp | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 10f365dd7..f0b78a778 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -301,7 +301,6 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign ImGui::PopStyleColor(); Vec3f translation = m_state.get_translation().cast(); Vec3f rotation = m_state.get_rotation(); - unsigned int button = m_state.get_button(); ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index c06f0863e..a3eab740f 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -47,8 +47,10 @@ class Mouse3DController template struct InputQueue { - size_t max_size{ 20 }; + size_t max_size; std::queue queue; + + InputQueue() : max_size(20) {} }; InputQueue m_translation; From f4654e376eabed8f9a72de1a4d1107302342e1fc Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 10 Oct 2019 10:49:47 +0200 Subject: [PATCH 24/73] ENABLE_3DCONNEXION_DEVICES -> Slightly increased camera target movement limits --- src/slic3r/GUI/Camera.cpp | 15 ++++++++++++--- src/slic3r/GUI/Mouse3DController.cpp | 21 ++------------------- src/slic3r/GUI/Mouse3DController.hpp | 28 ++++++++++++++++++++-------- 3 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index 4bb3e6ad5..e43b57636 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -85,10 +85,21 @@ void Camera::select_next_type() void Camera::set_target(const Vec3d& target) { +#if ENABLE_3DCONNEXION_DEVICES + // We may let these factors be customizable + static const double ScaleFactor = 1.1; + BoundingBoxf3 test_box = m_scene_box; + test_box.scale(ScaleFactor); + m_target = target; + m_target(0) = clamp(test_box.min(0), test_box.max(0), m_target(0)); + m_target(1) = clamp(test_box.min(1), test_box.max(1), m_target(1)); + m_target(2) = clamp(test_box.min(2), test_box.max(2), m_target(2)); +#else m_target = target; m_target(0) = clamp(m_scene_box.min(0), m_scene_box.max(0), m_target(0)); m_target(1) = clamp(m_scene_box.min(1), m_scene_box.max(1), m_target(1)); m_target(2) = clamp(m_scene_box.min(2), m_scene_box.max(2), m_target(2)); +#endif // ENABLE_3DCONNEXION_DEVICES } void Camera::set_theta(float theta, bool apply_limit) @@ -117,9 +128,7 @@ void Camera::set_zoom(double zoom) zoom = std::max(zoom, zoom_min * 0.7); // Don't allow to zoom too close to the scene. - zoom = std::min(zoom, 100.0); - - m_zoom = zoom; + m_zoom = std::min(zoom, 100.0); } #else void Camera::set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index f0b78a778..8cf7be33e 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -64,13 +64,11 @@ Mouse3DController::State::State() : m_translation_params(DefaultTranslationScale, DefaultTranslationDeadzone) , m_rotation_params(DefaultRotationScale, DefaultRotationDeadzone) , m_mouse_wheel_counter(0) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT , m_translation_queue_max_size(0) , m_rotation_queue_max_size(0) , m_buttons_queue_max_size(0) #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { } @@ -289,22 +287,17 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign m_state.set_rotation_deadzone(rotation_deadzone); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ImGui::Separator(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ImGui::Separator(); ImGui::PushStyleColor(ImGuiCol_Text, color); imgui.text("DEBUG:"); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ imgui.text("Vectors:"); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ImGui::PopStyleColor(); Vec3f translation = m_state.get_translation().cast(); Vec3f rotation = m_state.get_rotation(); ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ImGui::PushStyleColor(ImGuiCol_Text, color); imgui.text("Queue size:"); ImGui::PopStyleColor(); @@ -317,6 +310,7 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign ImGui::InputInt2("Rotation##4", rotation_size, ImGuiInputTextFlags_ReadOnly); ImGui::InputInt2("Buttons", buttons_size, ImGuiInputTextFlags_ReadOnly); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_QUEUE_MAX_SIZE int queue_size = (int)m_state.get_queues_max_size(); if (ImGui::InputInt("Max size", &queue_size, 1, 1, ImGuiInputTextFlags_ReadOnly)) @@ -351,10 +345,8 @@ bool Mouse3DController::connect_device() unsigned short product_id = 0; #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ unsigned short usage_page = 0; unsigned short usage = 0; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ hid_device_info* cur = devices; while (cur != nullptr) { @@ -387,12 +379,10 @@ bool Mouse3DController::connect_device() if (_3DCONNEXION_DEVICES[i] == current->product_id) { product_id = current->product_id; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT usage_page = current->usage_page; usage = current->usage; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ break; } } @@ -418,21 +408,16 @@ bool Mouse3DController::connect_device() if (m_device != nullptr) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::vector manufacturer(1024, 0); hid_get_manufacturer_string(m_device, manufacturer.data(), 1024); m_device_str = boost::nowide::narrow(manufacturer.data()); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::vector product(1024, 0); hid_get_product_string(m_device, product.data(), 1024); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_device_str += "/" + boost::nowide::narrow(product.data()); -// m_device_str = boost::nowide::narrow(product.data()); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ BOOST_LOG_TRIVIAL(info) << "Connected device: " << m_device_str; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT std::cout << std::endl << "Connected device:" << std::endl; std::cout << "Manufacturer/product..........: " << m_device_str << std::endl; @@ -441,8 +426,6 @@ bool Mouse3DController::connect_device() std::cout << "Usage page....................: " << usage_page << std::endl; std::cout << "Usage.........................: " << usage << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - // get device parameters from the config, if present double translation_speed = 1.0; diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index a3eab740f..f31bdc913 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -78,20 +78,18 @@ class Mouse3DController // GLCanvas3D::on_idle() through the call to the apply() method unsigned int m_mouse_wheel_counter; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT unsigned int m_translation_queue_max_size; unsigned int m_rotation_queue_max_size; unsigned int m_buttons_queue_max_size; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ public: State(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void append_translation(const Vec3d& translation) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_QUEUE_MAX_SIZE while (m_translation.queue.size() >= m_translation.max_size) { @@ -102,15 +100,19 @@ class Mouse3DController m_translation_queue_max_size = std::max(m_translation_queue_max_size, (unsigned int)m_translation.queue.size()); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_translation.push(translation); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT m_translation_queue_max_size = std::max(m_translation_queue_max_size, (unsigned int)m_translation.size()); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_QUEUE_MAX_SIZE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } void append_rotation(const Vec3f& rotation) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_QUEUE_MAX_SIZE while (m_rotation.queue.size() >= m_rotation.max_size) { @@ -123,17 +125,21 @@ class Mouse3DController if (rotation(0) != 0.0f) ++m_mouse_wheel_counter; #else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_rotation.push(rotation); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT m_rotation_queue_max_size = std::max(m_rotation_queue_max_size, (unsigned int)m_rotation.size()); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT if (rotation(0) != 0.0f) ++m_mouse_wheel_counter; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_QUEUE_MAX_SIZE - } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + } void append_button(unsigned int id) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_QUEUE_MAX_SIZE while (m_buttons.queue.size() >= m_buttons.max_size) { @@ -144,13 +150,15 @@ class Mouse3DController m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, (unsigned int)m_buttons.queue.size()); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_buttons.push(id); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, (unsigned int)m_buttons.size()); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -#endif // ENABLE_QUEUE_MAX_SIZE - } //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // ENABLE_QUEUE_MAX_SIZE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + } //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_QUEUE_MAX_SIZE @@ -158,9 +166,11 @@ class Mouse3DController bool has_rotation() const { return !m_rotation.queue.empty(); } bool has_button() const { return !m_buttons.queue.empty(); } #else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool has_translation() const { return !m_translation.empty(); } bool has_rotation() const { return !m_rotation.empty(); } bool has_button() const { return !m_buttons.empty(); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_QUEUE_MAX_SIZE //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@ -194,23 +204,25 @@ class Mouse3DController float get_rotation_deadzone() const { return m_rotation_params.deadzone; } void set_rotation_deadzone(float deadzone) { m_rotation_params.deadzone = deadzone; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_QUEUE_MAX_SIZE unsigned int get_translation_queue_size() const { return (unsigned int)m_translation.queue.size(); } unsigned int get_rotation_queue_size() const { return (unsigned int)m_rotation.queue.size(); } unsigned int get_buttons_queue_size() const { return (unsigned int)m_buttons.queue.size(); } #else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ unsigned int get_translation_queue_size() const { return (unsigned int)m_translation.size(); } unsigned int get_rotation_queue_size() const { return (unsigned int)m_rotation.size(); } unsigned int get_buttons_queue_size() const { return (unsigned int)m_buttons.size(); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_QUEUE_MAX_SIZE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ unsigned int get_translation_queue_max_size() const { return m_translation_queue_max_size; } unsigned int get_rotation_queue_max_size() const { return m_rotation_queue_max_size; } unsigned int get_buttons_queue_max_size() const { return m_buttons_queue_max_size; } #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_QUEUE_MAX_SIZE From 7caa596b95dbbdfd2a419dbcf55670b679d5965d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 10 Oct 2019 11:55:17 +0200 Subject: [PATCH 25/73] ENABLE_3DCONNEXION_DEVICES -> Refactoring and code cleanup --- src/libslic3r/Technologies.hpp | 2 - src/slic3r/GUI/Mouse3DController.cpp | 103 ++++++++-------- src/slic3r/GUI/Mouse3DController.hpp | 171 ++++----------------------- 3 files changed, 72 insertions(+), 204 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index f64bea172..da982c49a 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -39,8 +39,6 @@ // Enabled 3Dconnexion devices #define ENABLE_3DCONNEXION_DEVICES (1 && ENABLE_2_0_0_ALPHA1) -// Enabled 3Dconnexion devices debug output -#define ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT (1 && ENABLE_3DCONNEXION_DEVICES) #endif // _technologies_h_ diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 8cf7be33e..045e42e90 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -72,19 +72,45 @@ Mouse3DController::State::State() { } +void Mouse3DController::State::append_translation(const Vec3d& translation) +{ + while (m_translation.queue.size() >= m_translation.max_size) + { + m_translation.queue.pop(); + } + m_translation.queue.push(translation); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_translation_queue_max_size = std::max(m_translation_queue_max_size, m_translation.queue.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +} + +void Mouse3DController::State::append_rotation(const Vec3f& rotation) +{ + while (m_rotation.queue.size() >= m_rotation.max_size) + { + m_rotation.queue.pop(); + } + m_rotation.queue.push(rotation); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_rotation_queue_max_size = std::max(m_rotation_queue_max_size, m_rotation.queue.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + if (rotation(0) != 0.0f) + ++m_mouse_wheel_counter; +} + +void Mouse3DController::State::append_button(unsigned int id) +{ + m_buttons.push(id); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, m_buttons.size()); +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +} + bool Mouse3DController::State::process_mouse_wheel() { if (m_mouse_wheel_counter == 0) return false; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE else if (!m_rotation.queue.empty()) -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - else if (!m_rotation.empty()) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { --m_mouse_wheel_counter; return true; @@ -96,6 +122,21 @@ bool Mouse3DController::State::process_mouse_wheel() } } +void Mouse3DController::State::set_queues_max_size(size_t size) +{ + if (size > 0) + { + m_translation.max_size = size; + m_rotation.max_size = size; + +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + m_translation_queue_max_size = 0; + m_rotation_queue_max_size = 0; + m_buttons_queue_max_size = 0; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + } +} + bool Mouse3DController::State::apply(Camera& camera) { if (!wxGetApp().IsActive()) @@ -105,26 +146,14 @@ bool Mouse3DController::State::apply(Camera& camera) if (has_translation()) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE const Vec3d& translation = m_translation.queue.front(); camera.set_target(camera.get_target() + m_translation_params.scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up())); m_translation.queue.pop(); -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - const Vec3d& translation = m_translation.front(); - camera.set_target(camera.get_target() + m_translation_params.scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up())); - m_translation.pop(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ret = true; } if (has_rotation()) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE const Vec3f& rotation = m_rotation.queue.front(); float theta = m_rotation_params.scale * rotation(0); float phi = m_rotation_params.scale * rotation(2); @@ -132,36 +161,11 @@ bool Mouse3DController::State::apply(Camera& camera) camera.phi += sign * phi; camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); m_rotation.queue.pop(); -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - const Vec3f& rotation = m_rotation.front(); - float theta = m_rotation_params.scale * rotation(0); - float phi = m_rotation_params.scale * rotation(2); - float sign = camera.inverted_phi ? -1.0f : 1.0f; - camera.phi += sign * phi; - camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); - m_rotation.pop(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - ret = true; } if (has_button()) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE - unsigned int button = m_buttons.queue.front(); - switch (button) - { - case 0: { camera.update_zoom(1.0); break; } - case 1: { camera.update_zoom(-1.0); break; } - default: { break; } - } - m_buttons.queue.pop(); -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ unsigned int button = m_buttons.front(); switch (button) { @@ -170,9 +174,6 @@ bool Mouse3DController::State::apply(Camera& camera) default: { break; } } m_buttons.pop(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ret = true; } @@ -310,16 +311,12 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign ImGui::InputInt2("Rotation##4", rotation_size, ImGuiInputTextFlags_ReadOnly); ImGui::InputInt2("Buttons", buttons_size, ImGuiInputTextFlags_ReadOnly); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE int queue_size = (int)m_state.get_queues_max_size(); if (ImGui::InputInt("Max size", &queue_size, 1, 1, ImGuiInputTextFlags_ReadOnly)) { if (queue_size > 0) m_state.set_queues_max_size(queue_size); } -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT imgui.end(); @@ -424,7 +421,7 @@ bool Mouse3DController::connect_device() std::cout << "Manufacturer id/product id....: " << vendor_id << "/" << product_id << std::endl; std::cout << "Manufacturer id/product id hex: " << std::hex << vendor_id << "/" << product_id << std::dec << std::endl; std::cout << "Usage page....................: " << usage_page << std::endl; - std::cout << "Usage.........................: " << usage << std::endl; + std::cout << "Usage.........................: " << usage << std::endl << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT // get device parameters from the config, if present diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index f31bdc913..916983e22 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -2,6 +2,10 @@ #define slic3r_Mouse3DController_hpp_ #if ENABLE_3DCONNEXION_DEVICES + +// Enabled debug output and extended imgui dialog +#define ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT 0 + #include "libslic3r/Point.hpp" #include "hidapi.h" @@ -11,10 +15,6 @@ #include #include -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#define ENABLE_QUEUE_MAX_SIZE 1 -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - namespace Slic3r { namespace GUI { @@ -42,29 +42,21 @@ class Mouse3DController CustomParameters(Number scale, Number deadzone) : scale(scale), deadzone(deadzone) {} }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE template struct InputQueue { size_t max_size; std::queue queue; - InputQueue() : max_size(20) {} + // The default value of 5 for max_size seems to work fine on all platforms + // The effects of changing this value can be tested by setting ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT to 1 + // and playing with the imgui dialog which shows by pressing CTRL+M + InputQueue() : max_size(5) {} }; InputQueue m_translation; InputQueue m_rotation; - InputQueue m_buttons; -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - std::queue m_translation; - std::queue m_rotation; std::queue m_buttons; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - CustomParameters m_translation_params; CustomParameters m_rotation_params; @@ -79,116 +71,21 @@ class Mouse3DController unsigned int m_mouse_wheel_counter; #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - unsigned int m_translation_queue_max_size; - unsigned int m_rotation_queue_max_size; - unsigned int m_buttons_queue_max_size; + size_t m_translation_queue_max_size; + size_t m_rotation_queue_max_size; + size_t m_buttons_queue_max_size; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT public: State(); - void append_translation(const Vec3d& translation) - { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE - while (m_translation.queue.size() >= m_translation.max_size) - { - m_translation.queue.pop(); - } - m_translation.queue.push(translation); -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - m_translation_queue_max_size = std::max(m_translation_queue_max_size, (unsigned int)m_translation.queue.size()); -#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - m_translation.push(translation); -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - m_translation_queue_max_size = std::max(m_translation_queue_max_size, (unsigned int)m_translation.size()); -#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - } + void append_translation(const Vec3d& translation); + void append_rotation(const Vec3f& rotation); + void append_button(unsigned int id); - void append_rotation(const Vec3f& rotation) - { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE - while (m_rotation.queue.size() >= m_rotation.max_size) - { - m_rotation.queue.pop(); - } - m_rotation.queue.push(rotation); -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - m_rotation_queue_max_size = std::max(m_rotation_queue_max_size, (unsigned int)m_rotation.queue.size()); -#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - if (rotation(0) != 0.0f) - ++m_mouse_wheel_counter; -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - m_rotation.push(rotation); -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - m_rotation_queue_max_size = std::max(m_rotation_queue_max_size, (unsigned int)m_rotation.size()); -#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - if (rotation(0) != 0.0f) - ++m_mouse_wheel_counter; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - } - - void append_button(unsigned int id) - { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE - while (m_buttons.queue.size() >= m_buttons.max_size) - { - m_buttons.queue.pop(); - } - m_buttons.queue.push(id); -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, (unsigned int)m_buttons.queue.size()); -#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - m_buttons.push(id); -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, (unsigned int)m_buttons.size()); -#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - } - -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE bool has_translation() const { return !m_translation.queue.empty(); } bool has_rotation() const { return !m_rotation.queue.empty(); } - bool has_button() const { return !m_buttons.queue.empty(); } -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - bool has_translation() const { return !m_translation.empty(); } - bool has_rotation() const { return !m_rotation.empty(); } bool has_button() const { return !m_buttons.empty(); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE - Vec3d get_translation() const { return has_translation() ? m_translation.queue.front() : Vec3d::Zero(); } - Vec3f get_rotation() const { return has_rotation() ? m_rotation.queue.front() : Vec3f::Zero(); } - unsigned int get_button() const { return has_button() ? m_buttons.queue.front() : 0; } -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - Vec3d get_translation() const { return has_translation() ? m_translation.front() : Vec3d::Zero(); } - Vec3f get_rotation() const { return has_rotation() ? m_rotation.front() : Vec3f::Zero(); } - unsigned int get_button() const { return has_button() ? m_buttons.front() : 0; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT bool process_mouse_wheel(); @@ -205,45 +102,21 @@ class Mouse3DController void set_rotation_deadzone(float deadzone) { m_rotation_params.deadzone = deadzone; } #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE + Vec3d get_translation() const { return has_translation() ? m_translation.queue.front() : Vec3d::Zero(); } + Vec3f get_rotation() const { return has_rotation() ? m_rotation.queue.front() : Vec3f::Zero(); } + unsigned int get_button() const { return has_button() ? m_buttons.front() : 0; } + unsigned int get_translation_queue_size() const { return (unsigned int)m_translation.queue.size(); } unsigned int get_rotation_queue_size() const { return (unsigned int)m_rotation.queue.size(); } - unsigned int get_buttons_queue_size() const { return (unsigned int)m_buttons.queue.size(); } -#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - unsigned int get_translation_queue_size() const { return (unsigned int)m_translation.size(); } - unsigned int get_rotation_queue_size() const { return (unsigned int)m_rotation.size(); } unsigned int get_buttons_queue_size() const { return (unsigned int)m_buttons.size(); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - unsigned int get_translation_queue_max_size() const { return m_translation_queue_max_size; } - unsigned int get_rotation_queue_max_size() const { return m_rotation_queue_max_size; } - unsigned int get_buttons_queue_max_size() const { return m_buttons_queue_max_size; } + size_t get_translation_queue_max_size() const { return m_translation_queue_max_size; } + size_t get_rotation_queue_max_size() const { return m_rotation_queue_max_size; } + size_t get_buttons_queue_max_size() const { return m_buttons_queue_max_size; } #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#if ENABLE_QUEUE_MAX_SIZE size_t get_queues_max_size() const { return m_translation.max_size; } - void set_queues_max_size(size_t size) - { - if (size > 0) - { - std::cout << "New queues max size: " << size << std::endl; - - m_translation.max_size = size; - m_rotation.max_size = size; - m_buttons.max_size = size; - - m_translation_queue_max_size = 0; - m_rotation_queue_max_size = 0; - m_buttons_queue_max_size = 0; - } - } -#endif // ENABLE_QUEUE_MAX_SIZE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void set_queues_max_size(size_t size); // return true if any change to the camera took place bool apply(Camera& camera); From a7972bcc77733c0ae1d11ddbbb02e8c75dd9488d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 11 Oct 2019 09:16:20 +0200 Subject: [PATCH 26/73] ENABLE_3DCONNEXION_DEVICES -> Debug output of device features --- src/slic3r/GUI/Mouse3DController.cpp | 34 ++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 045e42e90..7e3be9047 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -115,11 +115,9 @@ bool Mouse3DController::State::process_mouse_wheel() --m_mouse_wheel_counter; return true; } - else - { - m_mouse_wheel_counter = 0; - return true; - } + + m_mouse_wheel_counter = 0; + return true; } void Mouse3DController::State::set_queues_max_size(size_t size) @@ -422,6 +420,22 @@ bool Mouse3DController::connect_device() std::cout << "Manufacturer id/product id hex: " << std::hex << vendor_id << "/" << product_id << std::dec << std::endl; std::cout << "Usage page....................: " << usage_page << std::endl; std::cout << "Usage.........................: " << usage << std::endl << std::endl; + + std::cout << "Feature reports:" << std::endl; + for (int i = 0; i < 256; ++i) + { + DataPacket packet = { 0 }; + packet[0] = (unsigned char)i; + int res = hid_get_feature_report(m_device, packet.data(), packet.size()); + if (res > 0) + { + for (int j = 0; j < res; ++j) + { + std::cout << " " << (int)packet[j]; + } + std::cout << std::endl; + } + } #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT // get device parameters from the config, if present @@ -434,9 +448,9 @@ bool Mouse3DController::connect_device() wxGetApp().app_config->get_mouse_device_rotation_speed(m_device_str, rotation_speed); wxGetApp().app_config->get_mouse_device_rotation_deadzone(m_device_str, rotation_deadzone); // clamp to valid values - m_state.set_translation_scale(State::DefaultTranslationScale* std::max(0.5, std::min(2.0, translation_speed))); + m_state.set_translation_scale(State::DefaultTranslationScale * std::max(0.5, std::min(2.0, translation_speed))); m_state.set_translation_deadzone(std::max(0.0, std::min(State::MaxTranslationDeadzone, translation_deadzone))); - m_state.set_rotation_scale(State::DefaultRotationScale* std::max(0.5f, std::min(2.0f, rotation_speed))); + m_state.set_rotation_scale(State::DefaultRotationScale * std::max(0.5f, std::min(2.0f, rotation_speed))); m_state.set_rotation_deadzone(std::max(0.0f, std::min(State::MaxRotationDeadzone, rotation_deadzone))); } @@ -505,8 +519,10 @@ void Mouse3DController::collect_input() updated = handle_packet(packet); else if (res == 13) updated = handle_wireless_packet(packet); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT else if (res > 0) std::cout << "Got unknown data packet of length: " << res << ", code:" << (int)packet[0] << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT if (updated) // ask for an idle event to update 3D scene @@ -540,7 +556,9 @@ bool Mouse3DController::handle_packet(const DataPacket& packet) } default: { +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT std::cout << "Got unknown data packet of code: " << (int)packet[0] << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT break; } } @@ -571,7 +589,9 @@ bool Mouse3DController::handle_wireless_packet(const DataPacket& packet) } default: { +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT std::cout << "Got unknown data packet of code: " << (int)packet[0] << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT break; } } From 1c05d88c008c9d2b27cc43722e322c8df5fd9b05 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 11 Oct 2019 15:29:57 +0200 Subject: [PATCH 27/73] ENABLE_3DCONNEXION_DEVICES -> Added support for 3Dconnexion universal receiver and battery level report --- resources/udev/90-3dconnexion.rules | 42 ++++++++++++++++----- src/slic3r/GUI/Mouse3DController.cpp | 55 ++++++++++++---------------- 2 files changed, 55 insertions(+), 42 deletions(-) diff --git a/resources/udev/90-3dconnexion.rules b/resources/udev/90-3dconnexion.rules index 035d17125..670d91790 100644 --- a/resources/udev/90-3dconnexion.rules +++ b/resources/udev/90-3dconnexion.rules @@ -1,23 +1,45 @@ # See src/slic3r/GUI/Mouse3DController.cpp for the list of devices # Logitech vendor devices -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c623", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c626", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c628", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c627", MODE="0666" SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c603", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62b", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c605", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c606", MODE="0666" SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c621", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c623", MODE="0666" SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c625", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c626", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c627", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c628", MODE="0666" SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c629", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62b", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62e", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62f", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c631", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c632", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c633", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c635", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c636", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c640", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c652", MODE="0666" # 3D Connexion vendor devices -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c623", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c626", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c628", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c627", MODE="0666" SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c603", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62b", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c605", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c606", MODE="0666" SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c621", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c623", MODE="0666" SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c625", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c626", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c627", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c628", MODE="0666" SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c629", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62b", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62e", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62f", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c631", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c632", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c633", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c635", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c636", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c640", MODE="0666" +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c652", MODE="0666" diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 7e3be9047..d75ad4390 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -46,8 +46,7 @@ static const std::vector _3DCONNEXION_DEVICES = 0xc635, /* 50741 spacemouse compact *TESTED* */ 0xc636, /* 50742 spacemouse module */ 0xc640, /* 50752 nulooq */ - -// 0xc652, /* 50770 3Dconnexion universal receiver */ + 0xc652, /* 50770 3Dconnexion universal receiver *TESTED* */ }; namespace Slic3r { @@ -336,12 +335,11 @@ bool Mouse3DController::connect_device() } // Searches for 1st connected 3Dconnexion device + std::string path = ""; unsigned short vendor_id = 0; unsigned short product_id = 0; #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - unsigned short usage_page = 0; - unsigned short usage = 0; hid_device_info* cur = devices; while (cur != nullptr) { @@ -371,13 +369,10 @@ bool Mouse3DController::connect_device() { for (size_t i = 0; i < _3DCONNEXION_DEVICES.size(); ++i) { - if (_3DCONNEXION_DEVICES[i] == current->product_id) + if ((_3DCONNEXION_DEVICES[i] == current->product_id) && (current->usage_page == 1) && (current->usage == 8)) { product_id = current->product_id; -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - usage_page = current->usage_page; - usage = current->usage; -#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + path = current->path; break; } } @@ -398,8 +393,8 @@ bool Mouse3DController::connect_device() if (vendor_id == 0) return false; - // Open the 3Dconnexion device using the VID, PID - m_device = hid_open(vendor_id, product_id, nullptr); + // Open the 3Dconnexion device using the device path + m_device = hid_open_path(path.c_str()); if (m_device != nullptr) { @@ -415,27 +410,9 @@ bool Mouse3DController::connect_device() #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT std::cout << std::endl << "Connected device:" << std::endl; - std::cout << "Manufacturer/product..........: " << m_device_str << std::endl; - std::cout << "Manufacturer id/product id....: " << vendor_id << "/" << product_id << std::endl; - std::cout << "Manufacturer id/product id hex: " << std::hex << vendor_id << "/" << product_id << std::dec << std::endl; - std::cout << "Usage page....................: " << usage_page << std::endl; - std::cout << "Usage.........................: " << usage << std::endl << std::endl; - - std::cout << "Feature reports:" << std::endl; - for (int i = 0; i < 256; ++i) - { - DataPacket packet = { 0 }; - packet[0] = (unsigned char)i; - int res = hid_get_feature_report(m_device, packet.data(), packet.size()); - if (res > 0) - { - for (int j = 0; j < res; ++j) - { - std::cout << " " << (int)packet[j]; - } - std::cout << std::endl; - } - } + std::cout << "Manufacturer/product: " << m_device_str << std::endl; + std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl; + std::cout << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")" << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT // get device parameters from the config, if present @@ -552,6 +529,13 @@ bool Mouse3DController::handle_packet(const DataPacket& packet) if (handle_packet_button(packet, 6)) return true; + break; + } + case 23: // Battery charge + { +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << m_device_str << " - battery level: " << (int)packet[1] << " percent" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT break; } default: @@ -585,6 +569,13 @@ bool Mouse3DController::handle_wireless_packet(const DataPacket& packet) if (handle_packet_button(packet, 12)) return true; + break; + } + case 23: // Battery charge + { +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << m_device_str << " - battery level: " << (int)packet[1] << " percent" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT break; } default: From e6403a74efaa15472689f460bd9a754d6129c348 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 11 Oct 2019 15:51:36 +0200 Subject: [PATCH 28/73] ENABLE_3DCONNEXION_DEVICES tech set as default --- src/libslic3r/Technologies.hpp | 9 ----- src/slic3r/GUI/AppConfig.cpp | 2 -- src/slic3r/GUI/AppConfig.hpp | 2 -- src/slic3r/GUI/Camera.cpp | 25 -------------- src/slic3r/GUI/Camera.hpp | 5 --- src/slic3r/GUI/GLCanvas3D.cpp | 51 ---------------------------- src/slic3r/GUI/GLCanvas3D.hpp | 8 ----- src/slic3r/GUI/ImGuiWrapper.cpp | 2 -- src/slic3r/GUI/KBShortcutsDialog.cpp | 2 -- src/slic3r/GUI/MainFrame.cpp | 2 -- src/slic3r/GUI/Mouse3DController.cpp | 4 --- src/slic3r/GUI/Mouse3DController.hpp | 6 +--- src/slic3r/GUI/Plater.cpp | 10 ------ src/slic3r/GUI/Plater.hpp | 2 -- 14 files changed, 1 insertion(+), 129 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index da982c49a..51d092094 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -32,13 +32,4 @@ #define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1) -//==================== -// 2.0.0.alpha1 techs -//==================== -#define ENABLE_2_0_0_ALPHA1 1 - -// Enabled 3Dconnexion devices -#define ENABLE_3DCONNEXION_DEVICES (1 && ENABLE_2_0_0_ALPHA1) - - #endif // _technologies_h_ diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp index c29fe1c3b..60f4edf47 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -271,7 +271,6 @@ void AppConfig::set_recent_projects(const std::vector& recent_proje } } -#if ENABLE_3DCONNEXION_DEVICES void AppConfig::set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone) { std::string key = std::string("mouse_device:") + name; @@ -345,7 +344,6 @@ bool AppConfig::get_mouse_device_rotation_deadzone(const std::string& name, floa deadzone = (float)::atof(it_val->second.c_str()); return true; } -#endif // ENABLE_3DCONNEXION_DEVICES void AppConfig::update_config_dir(const std::string &dir) { diff --git a/src/slic3r/GUI/AppConfig.hpp b/src/slic3r/GUI/AppConfig.hpp index fce0c0b39..355370450 100644 --- a/src/slic3r/GUI/AppConfig.hpp +++ b/src/slic3r/GUI/AppConfig.hpp @@ -131,13 +131,11 @@ public: std::vector get_recent_projects() const; void set_recent_projects(const std::vector& recent_projects); -#if ENABLE_3DCONNEXION_DEVICES void set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone); bool get_mouse_device_translation_speed(const std::string& name, double& speed); bool get_mouse_device_translation_deadzone(const std::string& name, double& deadzone); bool get_mouse_device_rotation_speed(const std::string& name, float& speed); bool get_mouse_device_rotation_deadzone(const std::string& name, float& deadzone); -#endif // ENABLE_3DCONNEXION_DEVICES static const std::string SECTION_FILAMENTS; static const std::string SECTION_MATERIALS; diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index e43b57636..9ecb5b4ad 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -85,7 +85,6 @@ void Camera::select_next_type() void Camera::set_target(const Vec3d& target) { -#if ENABLE_3DCONNEXION_DEVICES // We may let these factors be customizable static const double ScaleFactor = 1.1; BoundingBoxf3 test_box = m_scene_box; @@ -94,12 +93,6 @@ void Camera::set_target(const Vec3d& target) m_target(0) = clamp(test_box.min(0), test_box.max(0), m_target(0)); m_target(1) = clamp(test_box.min(1), test_box.max(1), m_target(1)); m_target(2) = clamp(test_box.min(2), test_box.max(2), m_target(2)); -#else - m_target = target; - m_target(0) = clamp(m_scene_box.min(0), m_scene_box.max(0), m_target(0)); - m_target(1) = clamp(m_scene_box.min(1), m_scene_box.max(1), m_target(1)); - m_target(2) = clamp(m_scene_box.min(2), m_scene_box.max(2), m_target(2)); -#endif // ENABLE_3DCONNEXION_DEVICES } void Camera::set_theta(float theta, bool apply_limit) @@ -114,7 +107,6 @@ void Camera::set_theta(float theta, bool apply_limit) } } -#if ENABLE_3DCONNEXION_DEVICES void Camera::update_zoom(double delta_zoom) { set_zoom(m_zoom / (1.0 - std::max(std::min(delta_zoom, 4.0), -4.0) * 0.1)); @@ -130,23 +122,6 @@ void Camera::set_zoom(double zoom) // Don't allow to zoom too close to the scene. m_zoom = std::min(zoom, 100.0); } -#else -void Camera::set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h) -{ - zoom = std::max(std::min(zoom, 4.0), -4.0) / 10.0; - zoom = m_zoom / (1.0 - zoom); - - // Don't allow to zoom too far outside the scene. - double zoom_min = calc_zoom_to_bounding_box_factor(max_box, canvas_w, canvas_h); - if (zoom_min > 0.0) - zoom = std::max(zoom, zoom_min * 0.7); - - // Don't allow to zoom too close to the scene. - zoom = std::min(zoom, 100.0); - - m_zoom = zoom; -} -#endif // ENABLE_3DCONNEXION_DEVICES bool Camera::select_view(const std::string& direction) { diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp index bf452833c..fae203a28 100644 --- a/src/slic3r/GUI/Camera.hpp +++ b/src/slic3r/GUI/Camera.hpp @@ -63,13 +63,8 @@ public: void set_theta(float theta, bool apply_limit); double get_zoom() const { return m_zoom; } -#if ENABLE_3DCONNEXION_DEVICES void update_zoom(double delta_zoom); void set_zoom(double zoom); -#else - void set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h); - void set_zoom(double zoom) { m_zoom = zoom; } -#endif // ENABLE_3DCONNEXION_DEVICES const BoundingBoxf3& get_scene_box() const { return m_scene_box; } void set_scene_box(const BoundingBoxf3& box) { m_scene_box = box; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index af0daef6d..8610db3fb 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -22,9 +22,7 @@ #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" #include "GUI_ObjectManipulation.hpp" -#if ENABLE_3DCONNEXION_DEVICES #include "Mouse3DController.hpp" -#endif // ENABLE_3DCONNEXION_DEVICES #include "I18N.hpp" #if ENABLE_RETINA_GL @@ -1394,11 +1392,7 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const BoundingBoxf3 GLCanvas3D::scene_bounding_box() const { BoundingBoxf3 bb = volumes_bounding_box(); -#if ENABLE_3DCONNEXION_DEVICES bb.merge(m_bed.get_bounding_box(true)); -#else - bb.merge(m_bed.get_bounding_box(false)); -#endif // ENABLE_3DCONNEXION_DEVICES if (m_config != nullptr) { @@ -1546,16 +1540,11 @@ void GLCanvas3D::render() return; } -#if ENABLE_3DCONNEXION_DEVICES const Size& cnv_size = get_canvas_size(); -#endif // ENABLE_3DCONNEXION_DEVICES if (m_camera.requires_zoom_to_bed) { zoom_to_bed(); -#if !ENABLE_3DCONNEXION_DEVICES - const Size& cnv_size = get_canvas_size(); -#endif // !ENABLE_3DCONNEXION_DEVICES _resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); m_camera.requires_zoom_to_bed = false; } @@ -1646,9 +1635,7 @@ void GLCanvas3D::render() m_camera.debug_render(); #endif // ENABLE_CAMERA_STATISTICS -#if ENABLE_3DCONNEXION_DEVICES wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); -#endif // ENABLE_3DCONNEXION_DEVICES wxGetApp().imgui()->render(); @@ -2350,17 +2337,14 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) m_dirty |= m_main_toolbar.update_items_state(); m_dirty |= m_undoredo_toolbar.update_items_state(); m_dirty |= m_view_toolbar.update_items_state(); -#if ENABLE_3DCONNEXION_DEVICES bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(m_camera); m_dirty |= mouse3d_controller_applied; -#endif // ENABLE_3DCONNEXION_DEVICES if (!m_dirty) return; _refresh_if_shown_on_screen(); -#if ENABLE_3DCONNEXION_DEVICES if (m_keep_dirty || mouse3d_controller_applied) { m_dirty = true; @@ -2368,10 +2352,6 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) } else m_dirty = false; -#else - if (m_keep_dirty) - m_dirty = true; -#endif // ENABLE_3DCONNEXION_DEVICES } void GLCanvas3D::on_char(wxKeyEvent& evt) @@ -2417,7 +2397,6 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) post_event(SimpleEvent(EVT_GLTOOLBAR_COPY)); break; -#if ENABLE_3DCONNEXION_DEVICES #ifdef __APPLE__ case 'm': case 'M': @@ -2430,7 +2409,6 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) m_dirty = true; break; } -#endif // ENABLE_3DCONNEXION_DEVICES #ifdef __APPLE__ case 'v': @@ -2499,19 +2477,11 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case 'B': case 'b': { zoom_to_bed(); break; } case 'I': -#if ENABLE_3DCONNEXION_DEVICES case 'i': { _update_camera_zoom(1.0); break; } -#else - case 'i': { set_camera_zoom(1.0); break; } -#endif // ENABLE_3DCONNEXION_DEVICES case 'K': case 'k': { m_camera.select_next_type(); m_dirty = true; break; } case 'O': -#if ENABLE_3DCONNEXION_DEVICES case 'o': { _update_camera_zoom(-1.0); break; } -#else - case 'o': { set_camera_zoom(-1.0); break; } -#endif // ENABLE_3DCONNEXION_DEVICES #if ENABLE_RENDER_PICKING_PASS case 'T': case 't': { @@ -2614,12 +2584,10 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) { -#if ENABLE_3DCONNEXION_DEVICES // try to filter out events coming from mouse 3d Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller(); if (controller.process_mouse_wheel()) return; -#endif // ENABLE_3DCONNEXION_DEVICES if (!m_initialized) return; @@ -2665,11 +2633,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) return; // Calculate the zoom delta and apply it to the current zoom factor -#if ENABLE_3DCONNEXION_DEVICES _update_camera_zoom((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta()); -#else - set_camera_zoom((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta()); -#endif // ENABLE_3DCONNEXION_DEVICES } void GLCanvas3D::on_timer(wxTimerEvent& evt) @@ -3451,15 +3415,6 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type) m_dirty = true; } -#if !ENABLE_3DCONNEXION_DEVICES -void GLCanvas3D::set_camera_zoom(double zoom) -{ - const Size& cnv_size = get_canvas_size(); - m_camera.set_zoom(zoom, _max_bounding_box(false, true), cnv_size.get_width(), cnv_size.get_height()); - m_dirty = true; -} -#endif // !ENABLE_3DCONNEXION_DEVICES - void GLCanvas3D::update_gizmos_on_off_state() { set_as_dirty(); @@ -3910,10 +3865,6 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) // updates camera m_camera.apply_viewport(0, 0, w, h); - -#if !ENABLE_3DCONNEXION_DEVICES - m_dirty = false; -#endif // !ENABLE_3DCONNEXION_DEVICES } BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_bed_model) const @@ -3941,13 +3892,11 @@ void GLCanvas3D::_zoom_to_box(const BoundingBoxf3& box) m_dirty = true; } -#if ENABLE_3DCONNEXION_DEVICES void GLCanvas3D::_update_camera_zoom(double zoom) { m_camera.update_zoom(zoom); m_dirty = true; } -#endif // ENABLE_3DCONNEXION_DEVICES void GLCanvas3D::_refresh_if_shown_on_screen() { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 5c3c26c1d..2338ebc90 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -486,9 +486,7 @@ public: void set_color_by(const std::string& value); const Camera& get_camera() const { return m_camera; } -#if ENABLE_3DCONNEXION_DEVICES Camera& get_camera() { return m_camera; } -#endif // ENABLE_3DCONNEXION_DEVICES BoundingBoxf3 volumes_bounding_box() const; BoundingBoxf3 scene_bounding_box() const; @@ -567,10 +565,6 @@ public: void do_flatten(const Vec3d& normal, const std::string& snapshot_type); void do_mirror(const std::string& snapshot_type); -#if !ENABLE_3DCONNEXION_DEVICES - void set_camera_zoom(double zoom); -#endif // !ENABLE_3DCONNEXION_DEVICES - void update_gizmos_on_off_state(); void reset_all_gizmos() { m_gizmos.reset_all_states(); } @@ -644,9 +638,7 @@ private: BoundingBoxf3 _max_bounding_box(bool include_gizmos, bool include_bed_model) const; void _zoom_to_box(const BoundingBoxf3& box); -#if ENABLE_3DCONNEXION_DEVICES void _update_camera_zoom(double zoom); -#endif // ENABLE_3DCONNEXION_DEVICES void _refresh_if_shown_on_screen(); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 7d7e82685..9dfe39bdd 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -529,10 +529,8 @@ void ImGuiWrapper::init_style() set_color(ImGuiCol_SliderGrab, COL_ORANGE_DARK); set_color(ImGuiCol_SliderGrabActive, COL_ORANGE_LIGHT); -#if ENABLE_3DCONNEXION_DEVICES // Separator set_color(ImGuiCol_Separator, COL_ORANGE_LIGHT); -#endif // ENABLE_3DCONNEXION_DEVICES } void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 4bd56ee76..268682b81 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -157,9 +157,7 @@ void KBShortcutsDialog::fill_shortcuts() plater_shortcuts.push_back(Shortcut("Z", L("Zoom to selected object"))); plater_shortcuts.push_back(Shortcut("I", L("Zoom in"))); plater_shortcuts.push_back(Shortcut("O", L("Zoom out"))); -#if ENABLE_3DCONNEXION_DEVICES plater_shortcuts.push_back(Shortcut(ctrl+"M", L("Show/Hide 3Dconnexion devices settings dialog"))); -#endif // ENABLE_3DCONNEXION_DEVICES plater_shortcuts.push_back(Shortcut("ESC", L("Unselect gizmo / Clear selection"))); #if ENABLE_RENDER_PICKING_PASS // Don't localize debugging texts. diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 8be99b6a6..3b5b1003f 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -24,9 +24,7 @@ #include "PrintHostDialogs.hpp" #include "wxExtensions.hpp" #include "GUI_ObjectList.hpp" -#if ENABLE_3DCONNEXION_DEVICES #include "Mouse3DController.hpp" -#endif // ENABLE_3DCONNEXION_DEVICES #include "I18N.hpp" #include diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index d75ad4390..631844c5c 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -1,8 +1,6 @@ #include "libslic3r/libslic3r.h" #include "Mouse3DController.hpp" -#if ENABLE_3DCONNEXION_DEVICES - #include "Camera.hpp" #include "GUI_App.hpp" #include "PresetBundle.hpp" @@ -647,5 +645,3 @@ bool Mouse3DController::handle_packet_button(const DataPacket& packet, unsigned } // namespace GUI } // namespace Slic3r - -#endif // ENABLE_3DCONNEXION_DEVICES diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 916983e22..5530328b7 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -1,9 +1,7 @@ #ifndef slic3r_Mouse3DController_hpp_ #define slic3r_Mouse3DController_hpp_ -#if ENABLE_3DCONNEXION_DEVICES - -// Enabled debug output and extended imgui dialog +// Enabled debug output to console and extended imgui dialog #define ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT 0 #include "libslic3r/Point.hpp" @@ -169,7 +167,5 @@ private: } // namespace GUI } // namespace Slic3r -#endif // ENABLE_3DCONNEXION_DEVICES - #endif // slic3r_Mouse3DController_hpp_ diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index cfe180248..f75dde498 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -62,9 +62,7 @@ #include "GUI_Preview.hpp" #include "3DBed.hpp" #include "Camera.hpp" -#if ENABLE_3DCONNEXION_DEVICES #include "Mouse3DController.hpp" -#endif // ENABLE_3DCONNEXION_DEVICES #include "Tab.hpp" #include "PresetBundle.hpp" #include "BackgroundSlicingProcess.hpp" @@ -1373,9 +1371,7 @@ struct Plater::priv Sidebar *sidebar; Bed3D bed; Camera camera; -#if ENABLE_3DCONNEXION_DEVICES Mouse3DController mouse3d_controller; -#endif // ENABLE_3DCONNEXION_DEVICES View3D* view3D; GLToolbar view_toolbar; Preview *preview; @@ -2107,9 +2103,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // updates camera type from .ini file camera.set_type(get_config("use_perspective_camera")); -#if ENABLE_3DCONNEXION_DEVICES mouse3d_controller.init(); -#endif // ENABLE_3DCONNEXION_DEVICES // Initialize the Undo / Redo stack with a first snapshot. this->take_snapshot(_(L("New Project"))); @@ -2117,9 +2111,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) Plater::priv::~priv() { -#if ENABLE_3DCONNEXION_DEVICES mouse3d_controller.shutdown(); -#endif // ENABLE_3DCONNEXION_DEVICES if (config != nullptr) delete config; @@ -5159,7 +5151,6 @@ const Camera& Plater::get_camera() const return p->camera; } -#if ENABLE_3DCONNEXION_DEVICES const Mouse3DController& Plater::get_mouse3d_controller() const { return p->mouse3d_controller; @@ -5169,7 +5160,6 @@ Mouse3DController& Plater::get_mouse3d_controller() { return p->mouse3d_controller; } -#endif // ENABLE_3DCONNEXION_DEVICES bool Plater::can_delete() const { return p->can_delete(); } bool Plater::can_delete_all() const { return p->can_delete_all(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index a87ba39b4..c327b4d91 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -260,10 +260,8 @@ public: void msw_rescale(); const Camera& get_camera() const; -#if ENABLE_3DCONNEXION_DEVICES const Mouse3DController& get_mouse3d_controller() const; Mouse3DController& get_mouse3d_controller(); -#endif // ENABLE_3DCONNEXION_DEVICES // ROII wrapper for suppressing the Undo / Redo snapshot to be taken. class SuppressSnapshots From a065c923029e104097c6145d4313327a5f2cb633 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 21 Oct 2019 11:06:18 +0200 Subject: [PATCH 29/73] Mouse3DController -> Enabled debug output to check device connection on Linux and Max --- src/slic3r/GUI/Mouse3DController.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 631844c5c..80bc6eb6c 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -345,7 +345,8 @@ bool Mouse3DController::connect_device() std::wcout << ((cur->manufacturer_string != nullptr) ? cur->manufacturer_string : L"Unknown"); std::cout << "::"; std::wcout << ((cur->product_string != nullptr) ? cur->product_string : L"Unknown"); - std::cout << "' code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")" << std::endl; + std::cout << "' code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")"; + std::cout << " usage page: " << cur->usage_page << " usage: " << cur->usage << std::endl; cur = cur->next; } @@ -411,6 +412,7 @@ bool Mouse3DController::connect_device() std::cout << "Manufacturer/product: " << m_device_str << std::endl; std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl; std::cout << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")" << std::endl; + std::cout << "Path................: '" << path << "'" << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT // get device parameters from the config, if present @@ -428,6 +430,16 @@ bool Mouse3DController::connect_device() m_state.set_rotation_scale(State::DefaultRotationScale * std::max(0.5f, std::min(2.0f, rotation_speed))); m_state.set_rotation_deadzone(std::max(0.0f, std::min(State::MaxRotationDeadzone, rotation_deadzone))); } +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + else + { + std::cout << std::endl << "Unable to connect to device:" << std::endl; + std::cout << "Manufacturer/product: " << m_device_str << std::endl; + std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl; + std::cout << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")" << std::endl; + std::cout << "Path................: '" << path << "'" << std::endl; + } +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT return (m_device != nullptr); } From 76a2f72e972ebe4190e723c6113b8425fe758a07 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 21 Oct 2019 11:12:56 +0200 Subject: [PATCH 30/73] Follow-up of a065c923029e104097c6145d4313327a5f2cb633 -> proper enabling of debug output --- src/slic3r/GUI/Mouse3DController.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 5530328b7..9812cb742 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -2,7 +2,7 @@ #define slic3r_Mouse3DController_hpp_ // Enabled debug output to console and extended imgui dialog -#define ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT 0 +#define ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT 1 #include "libslic3r/Point.hpp" From c47ca5a2c03c1f933cf151dd47f54d1e14cc0d89 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 21 Oct 2019 11:30:29 +0200 Subject: [PATCH 31/73] Mouse3DController -> Added debug output of device serial number --- src/slic3r/GUI/Mouse3DController.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 80bc6eb6c..0143b3928 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -346,6 +346,8 @@ bool Mouse3DController::connect_device() std::cout << "::"; std::wcout << ((cur->product_string != nullptr) ? cur->product_string : L"Unknown"); std::cout << "' code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")"; + std::cout << " serial number: "; + std::wcout << ((cur->serial_number != nullptr) ? cur->serial_number : L"Unknown"); std::cout << " usage page: " << cur->usage_page << " usage: " << cur->usage << std::endl; cur = cur->next; From 3ce3f33c9cfce023af856998ab9619641f69917d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 21 Oct 2019 14:21:51 +0200 Subject: [PATCH 32/73] Mouse3DController::connect_device() -> Modified logic to detect and select plugged device --- src/slic3r/GUI/Mouse3DController.cpp | 84 +++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 15 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 0143b3928..fae62ca7e 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -333,9 +333,13 @@ bool Mouse3DController::connect_device() } // Searches for 1st connected 3Dconnexion device - std::string path = ""; - unsigned short vendor_id = 0; - unsigned short product_id = 0; + struct DeviceData + { + std::string path{ "" }; + int interface_number{ 0 }; + unsigned short usage_page{ 0 }; + unsigned short usage{ 0 }; + }; #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT hid_device_info* cur = devices; @@ -346,17 +350,32 @@ bool Mouse3DController::connect_device() std::cout << "::"; std::wcout << ((cur->product_string != nullptr) ? cur->product_string : L"Unknown"); std::cout << "' code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")"; - std::cout << " serial number: "; + std::cout << " serial number: '"; std::wcout << ((cur->serial_number != nullptr) ? cur->serial_number : L"Unknown"); - std::cout << " usage page: " << cur->usage_page << " usage: " << cur->usage << std::endl; + std::cout << "' usage page: " << cur->usage_page << " usage: " << cur->usage << " interface number: " << cur->interface_number << std::endl; cur = cur->next; } #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + // When using 3Dconnexion universal receiver, multiple devices are detected sharing the same vendor_id and product_id. + // To choose from them the right one we use: + // On Windows and Mac: usage_page == 1 and usage == 8 + // On Linux: interface_number == 1, as usage_page and usage are not defined, see hidapi.h + // When only a single device is detected, as for wired connections, vendor_id and product_id are enough + + // First we count all the valid devices from the enumerated list, + hid_device_info* current = devices; + typedef std::pair DeviceIds; + typedef std::vector DeviceDataList; + typedef std::map DetectedDevices; + DetectedDevices detected_devices; while (current != nullptr) { + unsigned short vendor_id = 0; + unsigned short product_id = 0; + for (size_t i = 0; i < _3DCONNEXION_VENDORS.size(); ++i) { if (_3DCONNEXION_VENDORS[i] == current->vendor_id) @@ -370,28 +389,63 @@ bool Mouse3DController::connect_device() { for (size_t i = 0; i < _3DCONNEXION_DEVICES.size(); ++i) { - if ((_3DCONNEXION_DEVICES[i] == current->product_id) && (current->usage_page == 1) && (current->usage == 8)) + if (_3DCONNEXION_DEVICES[i] == current->product_id) { product_id = current->product_id; - path = current->path; - break; + DeviceIds detected_device(vendor_id, product_id); + DetectedDevices::iterator it = detected_devices.find(detected_device); + if (it == detected_devices.end()) + it = detected_devices.insert(DetectedDevices::value_type(detected_device, DeviceDataList())).first; + + it->second.push_back({ current->path, current->interface_number, current->usage_page, current->usage }); } } - - if (product_id == 0) - vendor_id = 0; } - if (vendor_id != 0) - break; - current = current->next; } // Free enumerated devices hid_free_enumeration(devices); - if (vendor_id == 0) + if (detected_devices.empty()) + return false; + + std::string path = ""; + unsigned short vendor_id = 0; + unsigned short product_id = 0; + + // Then we'll decide the choosing logic to apply in dependence of the device count and operating system + + for (const DetectedDevices::value_type& device : detected_devices) + { + if (device.second.size() == 1) + { + path = device.second.front().path; + vendor_id = device.first.first; + product_id = device.first.second; + break; + } + else + { + for (const DeviceData& data : device.second) + { +#if defined(__linux) + if (data.interface_number == 1) +#else + if ((data.usage_page == 1) && (data.usage == 8)) +#endif // __linux + { + path = data.path; + vendor_id = device.first.first; + product_id = device.first.second; + break; + } + } + } + } + + if (path.empty()) return false; // Open the 3Dconnexion device using the device path From d4f190a0c16292aefa8ecae18c0fc6a658c57adf Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 21 Oct 2019 15:20:36 +0200 Subject: [PATCH 33/73] Fixed build on Linux and Mac --- src/slic3r/GUI/Mouse3DController.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index fae62ca7e..78c0b6227 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -335,10 +335,17 @@ bool Mouse3DController::connect_device() // Searches for 1st connected 3Dconnexion device struct DeviceData { - std::string path{ "" }; - int interface_number{ 0 }; - unsigned short usage_page{ 0 }; - unsigned short usage{ 0 }; + std::string path; + int interface_number; + unsigned short usage_page; + unsigned short usage; + + DeviceData() + : path(""), interface_number(0), usage_page(0), usage(0) + {} + DeviceData(const std::string& path, int interface_number, unsigned short usage_page, unsigned short usage) + : path(path), interface_number(interface_number), usage_page(usage_page), usage(usage) + {} }; #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT @@ -397,7 +404,7 @@ bool Mouse3DController::connect_device() if (it == detected_devices.end()) it = detected_devices.insert(DetectedDevices::value_type(detected_device, DeviceDataList())).first; - it->second.push_back({ current->path, current->interface_number, current->usage_page, current->usage }); + it->second.emplace_back(current->path, current->interface_number, current->usage_page, current->usage); } } } From 55a4b99e44ea544612125ef3fb475a73579514f1 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 7 Nov 2019 11:48:59 +0100 Subject: [PATCH 34/73] Fixed bug into Camera::set_target() --- src/slic3r/GUI/Camera.cpp | 14 ++++++++------ src/slic3r/GUI/GLCanvas3D.cpp | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index 59f0a6c47..6ccb5af49 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -91,14 +91,16 @@ void Camera::select_next_type() void Camera::set_target(const Vec3d& target) { - // We may let these factors be customizable - static const double ScaleFactor = 1.1; BoundingBoxf3 test_box = m_scene_box; + test_box.translate(-m_scene_box.center()); + // We may let this factor be customizable + static const double ScaleFactor = 1.5; test_box.scale(ScaleFactor); - m_target = target; - m_target(0) = clamp(test_box.min(0), test_box.max(0), m_target(0)); - m_target(1) = clamp(test_box.min(1), test_box.max(1), m_target(1)); - m_target(2) = clamp(test_box.min(2), test_box.max(2), m_target(2)); + test_box.translate(m_scene_box.center()); + + m_target(0) = clamp(test_box.min(0), test_box.max(0), target(0)); + m_target(1) = clamp(test_box.min(1), test_box.max(1), target(1)); + m_target(2) = clamp(test_box.min(2), test_box.max(2), target(2)); } void Camera::set_theta(float theta, bool apply_limit) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7786f55f3..76990fb89 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1373,7 +1373,7 @@ void GLCanvas3D::set_model(Model* model) void GLCanvas3D::bed_shape_changed() { - m_camera.set_scene_box(scene_bounding_box()); + refresh_camera_scene_box(); m_camera.requires_zoom_to_bed = true; m_dirty = true; if (m_bed.is_prusa()) @@ -2116,7 +2116,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false)); } - m_camera.set_scene_box(scene_bounding_box()); + refresh_camera_scene_box(); if (m_selection.is_empty()) { From 6e4060569a833a61c257166d7906ce028d02a06d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 7 Nov 2019 15:55:45 +0100 Subject: [PATCH 35/73] 1) Added new tech ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE 2) Imgui dialog replaces texture for reset button and tooltip when layer editing is active --- src/libslic3r/PrintObject.cpp | 9 ++- src/libslic3r/Technologies.hpp | 3 + src/slic3r/GUI/GLCanvas3D.cpp | 102 +++++++++++++++++++++++++++++++-- src/slic3r/GUI/GLCanvas3D.hpp | 27 ++++++++- src/slic3r/GUI/Plater.cpp | 3 + 5 files changed, 132 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index c4ca46a8c..7819b8313 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1522,9 +1522,12 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c layer_height_profile.clear(); if (layer_height_profile.empty()) { - //layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes); - layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges); - updated = true; +//#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +// layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes); +//#else + layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges); +//#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + updated = true; } return updated; } diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 5d0a7592c..f8db1b7b3 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -42,4 +42,7 @@ #define ENABLE_THUMBNAIL_GENERATOR_DEBUG (0 && ENABLE_THUMBNAIL_GENERATOR) #define ENABLE_THUMBNAIL_GENERATOR_PNG_TO_GCODE (1 && ENABLE_THUMBNAIL_GENERATOR) +// Enable adaptive layer height profile +#define ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE (1 && ENABLE_2_2_0_ALPHA1) + #endif // _technologies_h_ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 99f61e51e..18ac3b10a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -150,7 +150,9 @@ GLCanvas3D::LayersEditing::~LayersEditing() } const float GLCanvas3D::LayersEditing::THICKNESS_BAR_WIDTH = 70.0f; +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE const float GLCanvas3D::LayersEditing::THICKNESS_RESET_BUTTON_HEIGHT = 22.0f; +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename) { @@ -217,13 +219,70 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const if (!m_enabled) return; +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + static const ImVec4 orange(0.757f, 0.404f, 0.216f, 1.0f); + + const Size& cnv_size = canvas.get_canvas_size(); + float canvas_w = (float)cnv_size.get_width(); + float canvas_h = (float)cnv_size.get_height(); + + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + imgui.set_next_window_pos(canvas_w - THICKNESS_BAR_WIDTH, canvas_h, ImGuiCond_Always, 1.0f, 1.0f); + imgui.set_next_window_bg_alpha(0.5f); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + + imgui.begin(_(L("Layer height profile")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); + + ImGui::PushStyleColor(ImGuiCol_Text, orange); + imgui.text(_(L("Left mouse button:"))); + ImGui::PopStyleColor(); + ImGui::SameLine(); + imgui.text(_(L("Add detail"))); + + ImGui::PushStyleColor(ImGuiCol_Text, orange); + imgui.text(_(L("Right mouse button:"))); + ImGui::PopStyleColor(); + ImGui::SameLine(); + imgui.text(_(L("Remove detail"))); + + ImGui::PushStyleColor(ImGuiCol_Text, orange); + imgui.text(_(L("Shift + Left mouse button:"))); + ImGui::PopStyleColor(); + ImGui::SameLine(); + imgui.text(_(L("Reset to base"))); + + ImGui::PushStyleColor(ImGuiCol_Text, orange); + imgui.text(_(L("Shift + Right mouse button:"))); + ImGui::PopStyleColor(); + ImGui::SameLine(); + imgui.text(_(L("Smoothing"))); + + ImGui::PushStyleColor(ImGuiCol_Text, orange); + imgui.text(_(L("Mouse wheel:"))); + ImGui::PopStyleColor(); + ImGui::SameLine(); + imgui.text(_(L("Increase/decrease edit area"))); + + ImGui::Separator(); + if (imgui.button(_(L("Reset")))) + wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE)); + + imgui.end(); + + ImGui::PopStyleVar(); + + const Rect& bar_rect = get_bar_rect_viewport(canvas); +#else const Rect& bar_rect = get_bar_rect_viewport(canvas); const Rect& reset_rect = get_reset_rect_viewport(canvas); _render_tooltip_texture(canvas, bar_rect, reset_rect); _render_reset_texture(reset_rect); - _render_active_object_annotations(canvas, bar_rect); - _render_profile(bar_rect); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + render_active_object_annotations(canvas, bar_rect); + render_profile(bar_rect); } float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) @@ -248,11 +307,13 @@ bool GLCanvas3D::LayersEditing::bar_rect_contains(const GLCanvas3D& canvas, floa return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom()); } +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool GLCanvas3D::LayersEditing::reset_rect_contains(const GLCanvas3D& canvas, float x, float y) { const Rect& rect = get_reset_rect_screen(canvas); return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom()); } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas) { @@ -260,9 +321,14 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas) float w = (float)cnv_size.get_width(); float h = (float)cnv_size.get_height(); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + return Rect(w - thickness_bar_width(canvas), 0.0f, w, h); +#else return Rect(w - thickness_bar_width(canvas), 0.0f, w, h - reset_button_height(canvas)); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE } +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE Rect GLCanvas3D::LayersEditing::get_reset_rect_screen(const GLCanvas3D& canvas) { const Size& cnv_size = canvas.get_canvas_size(); @@ -271,6 +337,7 @@ Rect GLCanvas3D::LayersEditing::get_reset_rect_screen(const GLCanvas3D& canvas) return Rect(w - thickness_bar_width(canvas), h - reset_button_height(canvas), w, h); } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) { @@ -281,9 +348,14 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) float zoom = (float)canvas.get_camera().get_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom); +#else return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE } +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas) { const Size& cnv_size = canvas.get_canvas_size(); @@ -295,13 +367,14 @@ Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom); } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - -bool GLCanvas3D::LayersEditing::_is_initialized() const +bool GLCanvas3D::LayersEditing::is_initialized() const { return m_shader.is_initialized(); } +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const { // TODO: do this with ImGui @@ -347,8 +420,9 @@ void GLCanvas3D::LayersEditing::_render_reset_texture(const Rect& reset_rect) co GLTexture::render_texture(m_reset_texture.get_id(), reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top()); } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const +void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const { m_shader.start_using(); @@ -379,7 +453,7 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas m_shader.stop_using(); } -void GLCanvas3D::LayersEditing::_render_profile(const Rect& bar_rect) const +void GLCanvas3D::LayersEditing::render_profile(const Rect& bar_rect) const { //FIXME show some kind of legend. @@ -557,6 +631,7 @@ float GLCanvas3D::LayersEditing::thickness_bar_width(const GLCanvas3D &canvas) * THICKNESS_BAR_WIDTH; } +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE float GLCanvas3D::LayersEditing::reset_button_height(const GLCanvas3D &canvas) { return @@ -567,6 +642,7 @@ float GLCanvas3D::LayersEditing::reset_button_height(const GLCanvas3D &canvas) #endif * THICKNESS_RESET_BUTTON_HEIGHT; } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE const Point GLCanvas3D::Mouse::Drag::Invalid_2D_Point(INT_MAX, INT_MAX); @@ -1118,6 +1194,9 @@ wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent); wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent); wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE #if ENABLE_THUMBNAIL_GENERATOR const double GLCanvas3D::DefaultCameraZoomToBoxMarginFactor = 1.25; @@ -1420,6 +1499,15 @@ bool GLCanvas3D::is_layers_editing_allowed() const return m_layers_editing.is_allowed(); } +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +void GLCanvas3D::reset_layer_height_profile() +{ + m_layers_editing.reset_layer_height_profile(*this); + m_layers_editing.state = LayersEditing::Completed; + m_dirty = true; +} +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + bool GLCanvas3D::is_reload_delayed() const { return m_reload_delayed; @@ -2814,6 +2902,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_layers_editing.state = LayersEditing::Editing; _perform_layer_editing_action(&evt); } +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE else if ((layer_editing_object_idx != -1) && m_layers_editing.reset_rect_contains(*this, pos(0), pos(1))) { if (evt.LeftDown()) @@ -2826,6 +2915,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; } } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE else if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled) { if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 8c2e6e9a5..da55210a8 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -104,6 +104,9 @@ wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent); wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent); wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class GLCanvas3D { @@ -153,13 +156,17 @@ private: private: static const float THICKNESS_BAR_WIDTH; +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static const float THICKNESS_RESET_BUTTON_HEIGHT; +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool m_enabled; Shader m_shader; unsigned int m_z_texture_id; +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE mutable GLTexture m_tooltip_texture; mutable GLTexture m_reset_texture; +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // Not owned by LayersEditing. const DynamicPrintConfig *m_config; // ModelObject for the currently selected object (Model::objects[last_object_id]). @@ -220,25 +227,35 @@ private: static float get_cursor_z_relative(const GLCanvas3D& canvas); static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static bool reset_rect_contains(const GLCanvas3D& canvas, float x, float y); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static Rect get_bar_rect_screen(const GLCanvas3D& canvas); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static Rect get_reset_rect_screen(const GLCanvas3D& canvas); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static Rect get_bar_rect_viewport(const GLCanvas3D& canvas); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static Rect get_reset_rect_viewport(const GLCanvas3D& canvas); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE float object_max_z() const { return m_object_max_z; } private: - bool _is_initialized() const; + bool is_initialized() const; void generate_layer_height_texture(); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void _render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const; void _render_reset_texture(const Rect& reset_rect) const; - void _render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const; - void _render_profile(const Rect& bar_rect) const; +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const; + void render_profile(const Rect& bar_rect) const; void update_slicing_parameters(); static float thickness_bar_width(const GLCanvas3D &canvas); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static float reset_button_height(const GLCanvas3D &canvas); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE }; struct Mouse @@ -500,6 +517,10 @@ public: bool is_layers_editing_enabled() const; bool is_layers_editing_allowed() const; +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void reset_layer_height_profile(); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + bool is_reload_delayed() const; void enable_layers_editing(bool enable); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index cdd35b720..44de82861 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2070,6 +2070,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_RESETGIZMOS, [this](SimpleEvent&) { reset_all_gizmos(); }); view3D_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); }); view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); }); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); }); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // 3DScene/Toolbar: view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this); From 68d6a458150ea194a9202853b106bc8d4b286ca6 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 8 Nov 2019 08:47:57 +0100 Subject: [PATCH 36/73] 3D connexion devides -> Added handling of 3 bytes long button packets --- src/slic3r/GUI/Mouse3DController.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 78c0b6227..96ef4f664 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -569,6 +569,9 @@ void Mouse3DController::collect_input() updated = handle_packet(packet); else if (res == 13) updated = handle_wireless_packet(packet); + else if ((res == 3) && (packet[0] == 3)) + // On Mac button packets can be 3 bytes long + updated = handle_packet(packet); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT else if (res > 0) std::cout << "Got unknown data packet of length: " << res << ", code:" << (int)packet[0] << std::endl; @@ -599,7 +602,8 @@ bool Mouse3DController::handle_packet(const DataPacket& packet) } case 3: // Button { - if (handle_packet_button(packet, 6)) + unsigned int size = packet.size(); + if (handle_packet_button(packet, packet.size() - 1)) return true; break; From 4320b8f7111c482c7ba4dae35102d6b31629202c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 8 Nov 2019 09:18:59 +0100 Subject: [PATCH 37/73] 3Dconnexion devices -> Fix into Mouse3DController::handle_packet_button --- src/slic3r/GUI/Mouse3DController.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 96ef4f664..352a5bf69 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -602,7 +602,6 @@ bool Mouse3DController::handle_packet(const DataPacket& packet) } case 3: // Button { - unsigned int size = packet.size(); if (handle_packet_button(packet, packet.size() - 1)) return true; @@ -708,7 +707,12 @@ bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigne bool Mouse3DController::handle_packet_button(const DataPacket& packet, unsigned int packet_size) { - unsigned int data = packet[1] | packet[2] << 8 | packet[3] << 16 | packet[4] << 24; + unsigned int data = 0; + for (unsigned int i = 1; i < packet_size; ++i) + { + data |= packet[i] << 8 * (i - 1); + } + const std::bitset<32> data_bits{ data }; for (size_t i = 0; i < data_bits.size(); ++i) { From b10d128ffd5414153908b91e46b3344cf3c17348 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 8 Nov 2019 11:51:56 +0100 Subject: [PATCH 38/73] 3Dconnexion devices -> Modified logic to select device on Linux --- src/slic3r/GUI/Mouse3DController.cpp | 102 +++++++++++++++++++++------ 1 file changed, 79 insertions(+), 23 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 352a5bf69..7a3443df0 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -336,39 +336,53 @@ bool Mouse3DController::connect_device() struct DeviceData { std::string path; - int interface_number; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// int interface_number; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ unsigned short usage_page; unsigned short usage; DeviceData() - : path(""), interface_number(0), usage_page(0), usage(0) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + : path(""), usage_page(0), usage(0) +// : path(""), interface_number(0), usage_page(0), usage(0) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ {} - DeviceData(const std::string& path, int interface_number, unsigned short usage_page, unsigned short usage) - : path(path), interface_number(interface_number), usage_page(usage_page), usage(usage) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + DeviceData(const std::string& path, unsigned short usage_page, unsigned short usage) + : path(path), usage_page(usage_page), usage(usage) +// DeviceData(const std::string& path, int interface_number, unsigned short usage_page, unsigned short usage) +// : path(path), interface_number(interface_number), usage_page(usage_page), usage(usage) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ {} }; -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - hid_device_info* cur = devices; - while (cur != nullptr) - { - std::cout << "Detected device '"; - std::wcout << ((cur->manufacturer_string != nullptr) ? cur->manufacturer_string : L"Unknown"); - std::cout << "::"; - std::wcout << ((cur->product_string != nullptr) ? cur->product_string : L"Unknown"); - std::cout << "' code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")"; - std::cout << " serial number: '"; - std::wcout << ((cur->serial_number != nullptr) ? cur->serial_number : L"Unknown"); - std::cout << "' usage page: " << cur->usage_page << " usage: " << cur->usage << " interface number: " << cur->interface_number << std::endl; - - cur = cur->next; - } -#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +// hid_device_info* cur = devices; +// while (cur != nullptr) +// { +// std::cout << "Detected device '"; +// std::wcout << ((cur->manufacturer_string != nullptr) ? cur->manufacturer_string : L"Unknown"); +// std::cout << "::"; +// std::wcout << ((cur->product_string != nullptr) ? cur->product_string : L"Unknown"); +// std::cout << "' code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")"; +// std::cout << " serial number: '"; +// std::wcout << ((cur->serial_number != nullptr) ? cur->serial_number : L"Unknown"); +// std::cout << "' usage page: " << cur->usage_page << " usage: " << cur->usage << " interface number: " << cur->interface_number << std::endl; +// +// cur = cur->next; +// } +//#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // When using 3Dconnexion universal receiver, multiple devices are detected sharing the same vendor_id and product_id. // To choose from them the right one we use: // On Windows and Mac: usage_page == 1 and usage == 8 - // On Linux: interface_number == 1, as usage_page and usage are not defined, see hidapi.h +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + // On Linux: as usage_page and usage are not defined (see hidapi.h) we try all detected devices until one is succesfully open +// // On Linux: interface_number == 1, as usage_page and usage are not defined, see hidapi.h +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // When only a single device is detected, as for wired connections, vendor_id and product_id are enough // First we count all the valid devices from the enumerated list, @@ -378,6 +392,11 @@ bool Mouse3DController::connect_device() typedef std::vector DeviceDataList; typedef std::map DetectedDevices; DetectedDevices detected_devices; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << std::endl << "Detected devices:" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ while (current != nullptr) { unsigned short vendor_id = 0; @@ -404,7 +423,22 @@ bool Mouse3DController::connect_device() if (it == detected_devices.end()) it = detected_devices.insert(DetectedDevices::value_type(detected_device, DeviceDataList())).first; - it->second.emplace_back(current->path, current->interface_number, current->usage_page, current->usage); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + it->second.emplace_back(current->path, current->usage_page, current->usage); +// it->second.emplace_back(current->path, current->interface_number, current->usage_page, current->usage); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::wcout << "\"" << ((current->manufacturer_string != nullptr) ? current->manufacturer_string : L"Unknown"); + std::cout << "/"; + std::wcout << ((current->product_string != nullptr) ? current->product_string : L"Unknown"); + std::cout << "\" code: " << current->vendor_id << "/" << current->product_id << " (" << std::hex << current->vendor_id << "/" << current->product_id << std::dec << ")"; + std::cout << " serial number: '"; + std::wcout << ((current->serial_number != nullptr) ? current->serial_number : L"Unknown"); + std::cout << "' usage page: " << current->usage_page << " usage: " << current->usage << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } } } @@ -435,10 +469,25 @@ bool Mouse3DController::connect_device() } else { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + bool found = false; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for (const DeviceData& data : device.second) { #if defined(__linux) - if (data.interface_number == 1) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + hid_device* test_device = hid_open_path(data.path.c_str()); + if (test_device != nullptr) + { + path = data.path; + vendor_id = device.first.first; + product_id = device.first.second; + found = true; + hid_close(test_device); + break; + } +// if (data.interface_number == 1) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #else if ((data.usage_page == 1) && (data.usage == 8)) #endif // __linux @@ -446,9 +495,16 @@ bool Mouse3DController::connect_device() path = data.path; vendor_id = device.first.first; product_id = device.first.second; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + found = true; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ break; } } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + if (found) + break; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } } From a87d83aeb18b5f130caafcc31ecb556bc2f8f735 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 8 Nov 2019 13:31:34 +0100 Subject: [PATCH 39/73] 3Dconnexion devices -> Another change in logic to select device --- src/slic3r/GUI/Mouse3DController.cpp | 30 ++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 7a3443df0..c1d85044c 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -462,7 +462,9 @@ bool Mouse3DController::connect_device() { if (device.second.size() == 1) { - path = device.second.front().path; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// path = device.second.front().path; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ vendor_id = device.first.first; product_id = device.first.second; break; @@ -484,6 +486,12 @@ bool Mouse3DController::connect_device() product_id = device.first.second; found = true; hid_close(test_device); +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << std::endl << "PASSED TEST device:" << std::endl; + std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl; + std::cout << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")" << std::endl; + std::cout << "Path................: '" << path << "'" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT break; } // if (data.interface_number == 1) @@ -508,11 +516,25 @@ bool Mouse3DController::connect_device() } } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (path.empty()) - return false; + { + if ((vendor_id != 0) && (product_id != 0)) + // Open the 3Dconnexion device using vendor_id and product_id + m_device = hid_open(vendor_id, product_id, nullptr); + else + return false; + } + else + // Open the 3Dconnexion device using the device path + m_device = hid_open_path(path.c_str()); - // Open the 3Dconnexion device using the device path - m_device = hid_open_path(path.c_str()); +// if (path.empty()) +// return false; +// +// // Open the 3Dconnexion device using the device path +// m_device = hid_open_path(path.c_str()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (m_device != nullptr) { From 08861250bf579b1f3164027c1d4541c2b5299548 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 8 Nov 2019 14:43:15 +0100 Subject: [PATCH 40/73] 3Dconnexion devices -> Added extra debug output --- src/slic3r/GUI/Mouse3DController.cpp | 41 +++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index c1d85044c..bf6dca20a 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -394,6 +394,7 @@ bool Mouse3DController::connect_device() DetectedDevices detected_devices; //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << std::endl << "======================================================================================================================================" << std::endl; std::cout << std::endl << "Detected devices:" << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@ -473,10 +474,19 @@ bool Mouse3DController::connect_device() { //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool found = false; +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for (const DeviceData& data : device.second) { -#if defined(__linux) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << "Test device: " << std::hex << device.first.first << std::dec << "/" << std::hex << device.first.second << std::dec << " \"" << data.path << "\""; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +#ifdef __linux__ +//#if defined(__linux) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ hid_device* test_device = hid_open_path(data.path.c_str()); if (test_device != nullptr) @@ -485,29 +495,36 @@ bool Mouse3DController::connect_device() vendor_id = device.first.first; product_id = device.first.second; found = true; - hid_close(test_device); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - std::cout << std::endl << "PASSED TEST device:" << std::endl; - std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl; - std::cout << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")" << std::endl; - std::cout << "Path................: '" << path << "'" << std::endl; + std::cout << "-> PASSED" << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + hid_close(test_device); break; } // if (data.interface_number == 1) //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #else if ((data.usage_page == 1) && (data.usage == 8)) -#endif // __linux +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // __linux__ +//#endif // __linux +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { path = data.path; vendor_id = device.first.first; product_id = device.first.second; //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ found = true; +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << "-> PASSED" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ break; } +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + else + std::cout << "-> NOT PASSED" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT } //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (found) @@ -520,14 +537,24 @@ bool Mouse3DController::connect_device() if (path.empty()) { if ((vendor_id != 0) && (product_id != 0)) + { // Open the 3Dconnexion device using vendor_id and product_id +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << std::endl << "Opening device: " << std::hex << vendor_id << std::dec << "/" << std::hex << product_id << std::dec << " using hid_open()" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT m_device = hid_open(vendor_id, product_id, nullptr); + } else return false; } else + { // Open the 3Dconnexion device using the device path +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << std::endl << "Opening device: " << std::hex << vendor_id << std::dec << "/" << std::hex << product_id << std::dec << "\"" << path << "\" using hid_open_path()" << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT m_device = hid_open_path(path.c_str()); + } // if (path.empty()) // return false; From 932b5be19680c9a30cc5076228b3aae3ff711820 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 8 Nov 2019 15:54:44 +0100 Subject: [PATCH 41/73] Fixed build on Linux --- src/slic3r/GUI/Mouse3DController.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index bf6dca20a..36da0536d 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -506,7 +506,6 @@ bool Mouse3DController::connect_device() #else if ((data.usage_page == 1) && (data.usage == 8)) //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#endif // __linux__ //#endif // __linux //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { @@ -521,6 +520,9 @@ bool Mouse3DController::connect_device() //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ break; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#endif // __linux__ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT else std::cout << "-> NOT PASSED" << std::endl; From dbc0996be10dfa5cf36858334d9b8a01b2fb16a9 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 11 Nov 2019 13:04:02 +0100 Subject: [PATCH 42/73] 3Dconnexion devices -> Disabled handling of device buttons / Added camera target to debug dialog / Added extra debug output --- src/slic3r/GUI/Mouse3DController.cpp | 123 +++++++++++---------------- src/slic3r/GUI/Mouse3DController.hpp | 2 + 2 files changed, 51 insertions(+), 74 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 36da0536d..516c7dc9e 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -58,7 +58,8 @@ const float Mouse3DController::State::MaxRotationDeadzone = (float)Mouse3DContro const float Mouse3DController::State::DefaultRotationDeadzone = 0.5f * Mouse3DController::State::MaxRotationDeadzone; Mouse3DController::State::State() - : m_translation_params(DefaultTranslationScale, DefaultTranslationDeadzone) + : m_buttons_enabled(false) + , m_translation_params(DefaultTranslationScale, DefaultTranslationDeadzone) , m_rotation_params(DefaultRotationScale, DefaultRotationDeadzone) , m_mouse_wheel_counter(0) #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT @@ -97,6 +98,9 @@ void Mouse3DController::State::append_rotation(const Vec3f& rotation) void Mouse3DController::State::append_button(unsigned int id) { + if (!m_buttons_enabled) + return; + m_buttons.push(id); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, m_buttons.size()); @@ -159,7 +163,7 @@ bool Mouse3DController::State::apply(Camera& camera) ret = true; } - if (has_button()) + if (m_buttons_enabled && has_button()) { unsigned int button = m_buttons.front(); switch (button) @@ -312,6 +316,13 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign if (queue_size > 0) m_state.set_queues_max_size(queue_size); } + + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text("Camera:"); + ImGui::PopStyleColor(); + Vec3f target = wxGetApp().plater()->get_camera().get_target().cast(); + ImGui::InputFloat3("Target", target.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT imgui.end(); @@ -336,53 +347,42 @@ bool Mouse3DController::connect_device() struct DeviceData { std::string path; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -// int interface_number; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ unsigned short usage_page; unsigned short usage; DeviceData() -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ : path(""), usage_page(0), usage(0) -// : path(""), interface_number(0), usage_page(0), usage(0) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ {} -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ DeviceData(const std::string& path, unsigned short usage_page, unsigned short usage) : path(path), usage_page(usage_page), usage(usage) -// DeviceData(const std::string& path, int interface_number, unsigned short usage_page, unsigned short usage) -// : path(path), interface_number(interface_number), usage_page(usage_page), usage(usage) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ {} + + bool has_valid_usage() const { return (usage_page == 1) && (usage == 8); } }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -// hid_device_info* cur = devices; -// while (cur != nullptr) -// { -// std::cout << "Detected device '"; -// std::wcout << ((cur->manufacturer_string != nullptr) ? cur->manufacturer_string : L"Unknown"); -// std::cout << "::"; -// std::wcout << ((cur->product_string != nullptr) ? cur->product_string : L"Unknown"); -// std::cout << "' code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")"; -// std::cout << " serial number: '"; -// std::wcout << ((cur->serial_number != nullptr) ? cur->serial_number : L"Unknown"); -// std::cout << "' usage page: " << cur->usage_page << " usage: " << cur->usage << " interface number: " << cur->interface_number << std::endl; -// -// cur = cur->next; -// } -//#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + hid_device_info* cur = devices; + std::cout << std::endl << "======================================================================================================================================" << std::endl; + std::cout << "Detected devices:" << std::endl; + while (cur != nullptr) + { + std::cout << "\""; + std::wcout << ((cur->manufacturer_string != nullptr) ? cur->manufacturer_string : L"Unknown"); + std::cout << "/"; + std::wcout << ((cur->product_string != nullptr) ? cur->product_string : L"Unknown"); + std::cout << "\" code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")"; + std::cout << " serial number: '"; + std::wcout << ((cur->serial_number != nullptr) ? cur->serial_number : L"Unknown"); + std::cout << "' usage page: " << cur->usage_page << " usage: " << cur->usage << " interface number: " << cur->interface_number << std::endl; + + cur = cur->next; + } +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT // When using 3Dconnexion universal receiver, multiple devices are detected sharing the same vendor_id and product_id. // To choose from them the right one we use: // On Windows and Mac: usage_page == 1 and usage == 8 -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // On Linux: as usage_page and usage are not defined (see hidapi.h) we try all detected devices until one is succesfully open -// // On Linux: interface_number == 1, as usage_page and usage are not defined, see hidapi.h -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // When only a single device is detected, as for wired connections, vendor_id and product_id are enough // First we count all the valid devices from the enumerated list, @@ -392,12 +392,9 @@ bool Mouse3DController::connect_device() typedef std::vector DeviceDataList; typedef std::map DetectedDevices; DetectedDevices detected_devices; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - std::cout << std::endl << "======================================================================================================================================" << std::endl; - std::cout << std::endl << "Detected devices:" << std::endl; + std::cout << std::endl << "Detected 3D connexion devices:" << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ while (current != nullptr) { unsigned short vendor_id = 0; @@ -424,12 +421,8 @@ bool Mouse3DController::connect_device() if (it == detected_devices.end()) it = detected_devices.insert(DetectedDevices::value_type(detected_device, DeviceDataList())).first; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ it->second.emplace_back(current->path, current->usage_page, current->usage); -// it->second.emplace_back(current->path, current->interface_number, current->usage_page, current->usage); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT std::wcout << "\"" << ((current->manufacturer_string != nullptr) ? current->manufacturer_string : L"Unknown"); std::cout << "/"; @@ -439,7 +432,6 @@ bool Mouse3DController::connect_device() std::wcout << ((current->serial_number != nullptr) ? current->serial_number : L"Unknown"); std::cout << "' usage page: " << current->usage_page << " usage: " << current->usage << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } } } @@ -463,31 +455,32 @@ bool Mouse3DController::connect_device() { if (device.second.size() == 1) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -// path = device.second.front().path; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - vendor_id = device.first.first; - product_id = device.first.second; - break; +#ifdef __linux__ + hid_device* test_device = hid_open(device.first.first, device.first.second); + if (test_device != nullptr) + { + hid_close(test_device); +#else + if (device.second.front().has_valid_usage()) + { +#endif // __linux__ + vendor_id = device.first.first; + product_id = device.first.second; + break; + } } else { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool found = false; #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT std::cout << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for (const DeviceData& data : device.second) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT std::cout << "Test device: " << std::hex << device.first.first << std::dec << "/" << std::hex << device.first.second << std::dec << " \"" << data.path << "\""; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #ifdef __linux__ -//#if defined(__linux) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ hid_device* test_device = hid_open_path(data.path.c_str()); if (test_device != nullptr) { @@ -501,41 +494,30 @@ bool Mouse3DController::connect_device() hid_close(test_device); break; } -// if (data.interface_number == 1) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #else - if ((data.usage_page == 1) && (data.usage == 8)) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//#endif // __linux -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + if (data.has_valid_usage()) { path = data.path; vendor_id = device.first.first; product_id = device.first.second; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ found = true; #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT std::cout << "-> PASSED" << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ break; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #endif // __linux__ -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT else std::cout << "-> NOT PASSED" << std::endl; #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + if (found) break; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (path.empty()) { if ((vendor_id != 0) && (product_id != 0)) @@ -558,13 +540,6 @@ bool Mouse3DController::connect_device() m_device = hid_open_path(path.c_str()); } -// if (path.empty()) -// return false; -// -// // Open the 3Dconnexion device using the device path -// m_device = hid_open_path(path.c_str()); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - if (m_device != nullptr) { std::vector manufacturer(1024, 0); diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 9812cb742..45616091f 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -56,6 +56,8 @@ class Mouse3DController InputQueue m_rotation; std::queue m_buttons; + bool m_buttons_enabled; + CustomParameters m_translation_params; CustomParameters m_rotation_params; From db5180919163743ecec3593b20b4fceb19414b1a Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 11 Nov 2019 13:41:50 +0100 Subject: [PATCH 43/73] Fixed build on Linux --- src/slic3r/GUI/Mouse3DController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 516c7dc9e..44bc0fd34 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -456,7 +456,7 @@ bool Mouse3DController::connect_device() if (device.second.size() == 1) { #ifdef __linux__ - hid_device* test_device = hid_open(device.first.first, device.first.second); + hid_device* test_device = hid_open(device.first.first, device.first.second, nullptr); if (test_device != nullptr) { hid_close(test_device); From b77ba32bb2171ae55254d5ac50f08d7f11911192 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 12 Nov 2019 14:18:43 +0100 Subject: [PATCH 44/73] Adaptive layer height profile -> Added Adaptive button to imgui dialog --- src/libslic3r/PrintObject.cpp | 5 +---- src/libslic3r/Slicing.cpp | 6 +++++- src/slic3r/GUI/GLCanvas3D.cpp | 22 ++++++++++++++++++++++ src/slic3r/GUI/GLCanvas3D.hpp | 5 +++++ src/slic3r/GUI/Plater.cpp | 1 + 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 7819b8313..bf97baaf6 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1522,11 +1522,8 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c layer_height_profile.clear(); if (layer_height_profile.empty()) { -//#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -// layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes); -//#else + //layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes); layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges); -//#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE updated = true; } return updated; diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index c62736ffe..1c1a1159f 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -609,7 +609,11 @@ int generate_layer_height_texture( const Vec3crd &color1 = palette_raw[idx1]; const Vec3crd &color2 = palette_raw[idx2]; coordf_t z = cell_to_z * coordf_t(cell); - assert(z >= lo && z <= hi); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + assert((lo - EPSILON <= z) && (z <= hi + EPSILON)); +#else + assert(z >= lo && z <= hi); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // Intensity profile to visualize the layers. coordf_t intensity = cos(M_PI * 0.7 * (mid - z) / h); // Color mapping from layer height to RGB. diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index fb01726b0..7b1dfb89a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -269,6 +269,10 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const if (imgui.button(_(L("Reset")))) wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE)); + ImGui::SameLine(); + if (imgui.button(_(L("Adaptive")))) + wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE)); + imgui.end(); ImGui::PopStyleVar(); @@ -570,6 +574,16 @@ void GLCanvas3D::LayersEditing::reset_layer_height_profile(GLCanvas3D& canvas) canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas) +{ + const_cast(m_model_object)->layer_height_profile.clear(); + m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, m_model_object->layer_config_ranges, m_model_object->volumes); + m_layers_texture.valid = false; + canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +} +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void GLCanvas3D::LayersEditing::generate_layer_height_texture() { this->update_slicing_parameters(); @@ -1196,6 +1210,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, SimpleEvent); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE #if ENABLE_THUMBNAIL_GENERATOR @@ -1506,6 +1521,13 @@ void GLCanvas3D::reset_layer_height_profile() m_layers_editing.state = LayersEditing::Completed; m_dirty = true; } + +void GLCanvas3D::adaptive_layer_height_profile() +{ + m_layers_editing.adaptive_layer_height_profile(*this); + m_layers_editing.state = LayersEditing::Completed; + m_dirty = true; +} #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool GLCanvas3D::is_reload_delayed() const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index da55210a8..d17174628 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -106,6 +106,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, SimpleEvent); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class GLCanvas3D @@ -224,6 +225,9 @@ private: void adjust_layer_height_profile(); void accept_changes(GLCanvas3D& canvas); void reset_layer_height_profile(GLCanvas3D& canvas); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void adaptive_layer_height_profile(GLCanvas3D& canvas); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static float get_cursor_z_relative(const GLCanvas3D& canvas); static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y); @@ -519,6 +523,7 @@ public: #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void reset_layer_height_profile(); + void adaptive_layer_height_profile(); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool is_reload_delayed() const; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 084d821ba..ac9c5cc4b 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2091,6 +2091,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); }); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); }); + view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(); }); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // 3DScene/Toolbar: From 0001ce3dab749d130fc2ead976aed3ba753d9b7f Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 13 Nov 2019 13:53:02 +0100 Subject: [PATCH 45/73] ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -> Refactoring of SlicingAdaptive to account for volumes' transformation --- src/libslic3r/Slicing.cpp | 24 ++++++++--- src/libslic3r/Slicing.hpp | 11 +++++- src/libslic3r/SlicingAdaptive.cpp | 66 ++++++++++++++++++++----------- src/libslic3r/SlicingAdaptive.hpp | 32 +++++++++++---- src/slic3r/GUI/GLCanvas3D.cpp | 12 +++--- 5 files changed, 104 insertions(+), 41 deletions(-) diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 1c1a1159f..a82bbc72a 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -224,24 +224,38 @@ std::vector layer_height_profile_from_ranges( // Based on the work of @platsch // Fill layer_height_profile by heights ensuring a prescribed maximum cusp height. +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +std::vector layer_height_profile_adaptive( + const SlicingParameters& slicing_params, + const ModelObject& object) +#else std::vector layer_height_profile_adaptive( const SlicingParameters &slicing_params, const t_layer_config_ranges & /* layer_config_ranges */, const ModelVolumePtrs &volumes) +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE { +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // 1) Initialize the SlicingAdaptive class with the object meshes. SlicingAdaptive as; as.set_slicing_parameters(slicing_params); - for (const ModelVolume *volume : volumes) + as.set_object(object); +#else + // 1) Initialize the SlicingAdaptive class with the object meshes. + SlicingAdaptive as; + as.set_slicing_parameters(slicing_params); + for (const ModelVolume* volume : volumes) if (volume->is_model_part()) as.add_mesh(&volume->mesh()); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + as.prepare(); // 2) Generate layers using the algorithm of @platsch // loop until we have at least one layer and the max slice_z reaches the object height //FIXME make it configurable // Cusp value: A maximum allowed distance from a corner of a rectangular extrusion to a chrodal line, in mm. - const coordf_t cusp_value = 0.2; // $self->config->get_value('cusp_value'); + const double cusp_value = 0.2; // $self->config->get_value('cusp_value'); std::vector layer_height_profile; layer_height_profile.push_back(0.); @@ -250,14 +264,14 @@ std::vector layer_height_profile_adaptive( layer_height_profile.push_back(slicing_params.first_object_layer_height); layer_height_profile.push_back(slicing_params.first_object_layer_height); } - coordf_t slice_z = slicing_params.first_object_layer_height; - coordf_t height = slicing_params.first_object_layer_height; + double slice_z = slicing_params.first_object_layer_height; + double height = slicing_params.first_object_layer_height; int current_facet = 0; while ((slice_z - height) <= slicing_params.object_print_z_height()) { height = 999; // Slic3r::debugf "\n Slice layer: %d\n", $id; // determine next layer height - coordf_t cusp_height = as.cusp_height(slice_z, cusp_value, current_facet); + double cusp_height = as.cusp_height((float)slice_z, (float)cusp_value, current_facet); // check for horizontal features and object size /* if($self->config->get_value('match_horizontal_surfaces')) { diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index 064363ec2..250d7baeb 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -18,8 +18,12 @@ namespace Slic3r class PrintConfig; class PrintObjectConfig; +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +class ModelObject; +#else class ModelVolume; typedef std::vector ModelVolumePtrs; +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // Parameters to guide object slicing and support generation. // The slicing parameters account for a raft and whether the 1st object layer is printed with a normal or a bridging flow @@ -138,11 +142,16 @@ extern std::vector layer_height_profile_from_ranges( const SlicingParameters &slicing_params, const t_layer_config_ranges &layer_config_ranges); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +extern std::vector layer_height_profile_adaptive( + const SlicingParameters& slicing_params, + const ModelObject& object); +#else extern std::vector layer_height_profile_adaptive( const SlicingParameters &slicing_params, const t_layer_config_ranges &layer_config_ranges, const ModelVolumePtrs &volumes); - +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE enum LayerHeightEditActionType : unsigned int { LAYER_HEIGHT_EDIT_ACTION_INCREASE = 0, diff --git a/src/libslic3r/SlicingAdaptive.cpp b/src/libslic3r/SlicingAdaptive.cpp index ad03b550b..a38752fbc 100644 --- a/src/libslic3r/SlicingAdaptive.cpp +++ b/src/libslic3r/SlicingAdaptive.cpp @@ -1,16 +1,22 @@ #include "libslic3r.h" +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +#include "Model.hpp" +#else #include "TriangleMesh.hpp" +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE #include "SlicingAdaptive.hpp" namespace Slic3r { +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void SlicingAdaptive::clear() { - m_meshes.clear(); + m_meshes.clear(); m_faces.clear(); m_face_normal_z.clear(); } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE std::pair face_z_span(const stl_facet *f) { @@ -21,21 +27,38 @@ std::pair face_z_span(const stl_facet *f) void SlicingAdaptive::prepare() { - // 1) Collect faces of all meshes. - int nfaces_total = 0; - for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + if (m_object == nullptr) + return; + + m_faces.clear(); + m_face_normal_z.clear(); + + m_mesh = m_object->raw_mesh(); + const ModelInstance* first_instance = m_object->instances.front(); + m_mesh.transform(first_instance->get_matrix(), first_instance->is_left_handed()); + + // 1) Collect faces from mesh. + m_faces.reserve(m_mesh.stl.stats.number_of_facets); + for (const stl_facet& face : m_mesh.stl.facet_start) + m_faces.emplace_back(&face); +#else + // 1) Collect faces of all meshes. + int nfaces_total = 0; + for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) nfaces_total += (*it_mesh)->stl.stats.number_of_facets; - m_faces.reserve(nfaces_total); - for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) - for (const stl_facet &face : (*it_mesh)->stl.facet_start) - m_faces.emplace_back(&face); + m_faces.reserve(nfaces_total); + for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) + for (const stl_facet& face : (*it_mesh)->stl.facet_start) + m_faces.emplace_back(&face); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // 2) Sort faces lexicographically by their Z span. std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) { - std::pair span1 = face_z_span(f1); + std::pair span1 = face_z_span(f1); std::pair span2 = face_z_span(f2); - return span1 < span2; - }); + return span1 < span2; + }); // 3) Generate Z components of the facet normals. m_face_normal_z.assign(m_faces.size(), 0.f); @@ -45,14 +68,14 @@ void SlicingAdaptive::prepare() float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet) { - float height = m_slicing_params.max_layer_height; + float height = (float)m_slicing_params.max_layer_height; bool first_hit = false; // find all facets intersecting the slice-layer int ordered_id = current_facet; for (; ordered_id < int(m_faces.size()); ++ ordered_id) { - std::pair zspan = face_z_span(m_faces[ordered_id]); - // facet's minimum is higher than slice_z -> end loop + std::pair zspan = face_z_span(m_faces[ordered_id]); + // facet's minimum is higher than slice_z -> end loop if (zspan.first >= z) break; // facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point @@ -77,8 +100,8 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet // check for sloped facets inside the determined layer and correct height if necessary if (height > m_slicing_params.min_layer_height) { for (; ordered_id < int(m_faces.size()); ++ ordered_id) { - std::pair zspan = face_z_span(m_faces[ordered_id]); - // facet's minimum is higher than slice_z + height -> end loop + std::pair zspan = face_z_span(m_faces[ordered_id]); + // facet's minimum is higher than slice_z + height -> end loop if (zspan.first >= z + height) break; @@ -122,19 +145,18 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet float SlicingAdaptive::horizontal_facet_distance(float z) { for (size_t i = 0; i < m_faces.size(); ++ i) { - std::pair zspan = face_z_span(m_faces[i]); - // facet's minimum is higher than max forward distance -> end loop + std::pair zspan = face_z_span(m_faces[i]); + // facet's minimum is higher than max forward distance -> end loop if (zspan.first > z + m_slicing_params.max_layer_height) break; // min_z == max_z -> horizontal facet - if (zspan.first > z && zspan.first == zspan.second) + if ((zspan.first > z) && (zspan.first == zspan.second)) return zspan.first - z; } // objects maximum? - return (z + m_slicing_params.max_layer_height > m_slicing_params.object_print_z_height()) ? - std::max(m_slicing_params.object_print_z_height() - z, 0.f) : - m_slicing_params.max_layer_height; + return (z + (float)m_slicing_params.max_layer_height > (float)m_slicing_params.object_print_z_height()) ? + std::max((float)m_slicing_params.object_print_z_height() - z, 0.f) : (float)m_slicing_params.max_layer_height; } }; // namespace Slic3r diff --git a/src/libslic3r/SlicingAdaptive.hpp b/src/libslic3r/SlicingAdaptive.hpp index bfd081d81..eccb298d0 100644 --- a/src/libslic3r/SlicingAdaptive.hpp +++ b/src/libslic3r/SlicingAdaptive.hpp @@ -5,29 +5,47 @@ #include "Slicing.hpp" #include "admesh/stl.h" +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +#include "TriangleMesh.hpp" +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE namespace Slic3r { +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +class ModelVolume; +#else class TriangleMesh; +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class SlicingAdaptive { public: - void clear(); - void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; } - void add_mesh(const TriangleMesh *mesh) { m_meshes.push_back(mesh); } - void prepare(); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void clear(); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; } +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void set_object(const ModelObject& object) { m_object = &object; } +#else + void add_mesh(const TriangleMesh* mesh) { m_meshes.push_back(mesh); } +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void prepare(); float cusp_height(float z, float cusp_value, int ¤t_facet); float horizontal_facet_distance(float z); protected: SlicingParameters m_slicing_params; - std::vector m_meshes; - // Collected faces of all meshes, sorted by raising Z of the bottom most face. +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + const ModelObject* m_object; + TriangleMesh m_mesh; +#else + std::vector m_meshes; +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + // Collected faces of all meshes, sorted by raising Z of the bottom most face. std::vector m_faces; - // Z component of face normals, normalized. + // Z component of face normals, normalized. std::vector m_face_normal_z; }; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7b1dfb89a..9647d252a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -266,13 +266,13 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const imgui.text(_(L("Increase/decrease edit area"))); ImGui::Separator(); - if (imgui.button(_(L("Reset")))) - wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE)); - - ImGui::SameLine(); if (imgui.button(_(L("Adaptive")))) wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE)); + ImGui::SameLine(); + if (imgui.button(_(L("Reset")))) + wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE)); + imgui.end(); ImGui::PopStyleVar(); @@ -577,8 +577,8 @@ void GLCanvas3D::LayersEditing::reset_layer_height_profile(GLCanvas3D& canvas) #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas) { - const_cast(m_model_object)->layer_height_profile.clear(); - m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, m_model_object->layer_config_ranges, m_model_object->volumes); + m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, *m_model_object); + const_cast(m_model_object)->layer_height_profile = m_layer_height_profile; m_layers_texture.valid = false; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } From 6eee31bf5a28264a48982eb32bfd428e7469af3d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 13 Nov 2019 15:06:17 +0100 Subject: [PATCH 46/73] ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -> Disabled unused code --- src/libslic3r/SlicingAdaptive.cpp | 2 ++ src/libslic3r/SlicingAdaptive.hpp | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/SlicingAdaptive.cpp b/src/libslic3r/SlicingAdaptive.cpp index a38752fbc..e64012553 100644 --- a/src/libslic3r/SlicingAdaptive.cpp +++ b/src/libslic3r/SlicingAdaptive.cpp @@ -140,6 +140,7 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet return height; } +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // Returns the distance to the next horizontal facet in Z-dir // to consider horizontal object features in slice thickness float SlicingAdaptive::horizontal_facet_distance(float z) @@ -158,5 +159,6 @@ float SlicingAdaptive::horizontal_facet_distance(float z) return (z + (float)m_slicing_params.max_layer_height > (float)m_slicing_params.object_print_z_height()) ? std::max((float)m_slicing_params.object_print_z_height() - z, 0.f) : (float)m_slicing_params.max_layer_height; } +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE }; // namespace Slic3r diff --git a/src/libslic3r/SlicingAdaptive.hpp b/src/libslic3r/SlicingAdaptive.hpp index eccb298d0..1d2996986 100644 --- a/src/libslic3r/SlicingAdaptive.hpp +++ b/src/libslic3r/SlicingAdaptive.hpp @@ -32,7 +32,9 @@ public: #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void prepare(); float cusp_height(float z, float cusp_value, int ¤t_facet); - float horizontal_facet_distance(float z); +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + float horizontal_facet_distance(float z); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE protected: SlicingParameters m_slicing_params; From 5baffdb9c20f55d5bc95b48a5e6355258a080ce9 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 14 Nov 2019 10:22:48 +0100 Subject: [PATCH 47/73] ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -> Customizable cusp value --- src/libslic3r/Slicing.cpp | 13 +++++-------- src/libslic3r/Slicing.hpp | 4 ++-- src/slic3r/GUI/GLCanvas3D.cpp | 20 ++++++++++++++------ src/slic3r/GUI/GLCanvas3D.hpp | 8 +++++--- src/slic3r/GUI/Plater.cpp | 2 +- 5 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index a82bbc72a..b7816fd04 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -225,9 +225,8 @@ std::vector layer_height_profile_from_ranges( // Based on the work of @platsch // Fill layer_height_profile by heights ensuring a prescribed maximum cusp height. #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -std::vector layer_height_profile_adaptive( - const SlicingParameters& slicing_params, - const ModelObject& object) +std::vector layer_height_profile_adaptive(const SlicingParameters& slicing_params, + const ModelObject& object, float cusp_value) #else std::vector layer_height_profile_adaptive( const SlicingParameters &slicing_params, @@ -253,11 +252,8 @@ std::vector layer_height_profile_adaptive( // 2) Generate layers using the algorithm of @platsch // loop until we have at least one layer and the max slice_z reaches the object height - //FIXME make it configurable - // Cusp value: A maximum allowed distance from a corner of a rectangular extrusion to a chrodal line, in mm. - const double cusp_value = 0.2; // $self->config->get_value('cusp_value'); - std::vector layer_height_profile; + std::vector layer_height_profile; layer_height_profile.push_back(0.); layer_height_profile.push_back(slicing_params.first_object_layer_height); if (slicing_params.first_object_layer_height_fixed()) { @@ -271,7 +267,8 @@ std::vector layer_height_profile_adaptive( height = 999; // Slic3r::debugf "\n Slice layer: %d\n", $id; // determine next layer height - double cusp_height = as.cusp_height((float)slice_z, (float)cusp_value, current_facet); + double cusp_height = as.cusp_height((float)slice_z, cusp_value, current_facet); + // check for horizontal features and object size /* if($self->config->get_value('match_horizontal_surfaces')) { diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index 250d7baeb..e25cbd326 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -143,9 +143,9 @@ extern std::vector layer_height_profile_from_ranges( const t_layer_config_ranges &layer_config_ranges); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -extern std::vector layer_height_profile_adaptive( +extern std::vector layer_height_profile_adaptive( const SlicingParameters& slicing_params, - const ModelObject& object); + const ModelObject& object, float cusp_value); #else extern std::vector layer_height_profile_adaptive( const SlicingParameters &slicing_params, diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 9647d252a..8ef0eaa13 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -130,6 +130,7 @@ GLCanvas3D::LayersEditing::LayersEditing() , m_object_max_z(0.f) , m_slicing_parameters(nullptr) , m_layer_height_profile_modified(false) + , m_adaptive_cusp(0.2f) , state(Unknown) , band_width(2.0f) , strength(0.005f) @@ -267,9 +268,16 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const ImGui::Separator(); if (imgui.button(_(L("Adaptive")))) - wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE)); + wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, m_adaptive_cusp)); ImGui::SameLine(); + imgui.text(_(L("Cusp (mm)"))); + ImGui::SameLine(); + ImGui::PushItemWidth(100.0f); + m_adaptive_cusp = std::min(m_adaptive_cusp, (float)m_slicing_parameters->max_layer_height); + ImGui::SliderFloat("", &m_adaptive_cusp, 0.0f, (float)m_slicing_parameters->max_layer_height, "%.2f"); + + ImGui::Separator(); if (imgui.button(_(L("Reset")))) wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE)); @@ -575,9 +583,9 @@ void GLCanvas3D::LayersEditing::reset_layer_height_profile(GLCanvas3D& canvas) } #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas) +void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp) { - m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, *m_model_object); + m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, *m_model_object, cusp); const_cast(m_model_object)->layer_height_profile = m_layer_height_profile; m_layers_texture.valid = false; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); @@ -1210,7 +1218,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); -wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE #if ENABLE_THUMBNAIL_GENERATOR @@ -1522,9 +1530,9 @@ void GLCanvas3D::reset_layer_height_profile() m_dirty = true; } -void GLCanvas3D::adaptive_layer_height_profile() +void GLCanvas3D::adaptive_layer_height_profile(float cusp) { - m_layers_editing.adaptive_layer_height_profile(*this); + m_layers_editing.adaptive_layer_height_profile(*this, cusp); m_layers_editing.state = LayersEditing::Completed; m_dirty = true; } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index d17174628..d1369d086 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -106,7 +106,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); -wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class GLCanvas3D @@ -179,6 +179,8 @@ private: std::vector m_layer_height_profile; bool m_layer_height_profile_modified; + mutable float m_adaptive_cusp; + class LayersTexture { public: @@ -226,7 +228,7 @@ private: void accept_changes(GLCanvas3D& canvas); void reset_layer_height_profile(GLCanvas3D& canvas); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - void adaptive_layer_height_profile(GLCanvas3D& canvas); + void adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static float get_cursor_z_relative(const GLCanvas3D& canvas); @@ -523,7 +525,7 @@ public: #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void reset_layer_height_profile(); - void adaptive_layer_height_profile(); + void adaptive_layer_height_profile(float cusp); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool is_reload_delayed() const; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ac9c5cc4b..e2b15602c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2091,7 +2091,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); }); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); }); - view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(); }); + view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); }); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // 3DScene/Toolbar: From f548a4d7d6fe328cf16f8b901758d60b8d94c2e3 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 15 Nov 2019 15:49:07 +0100 Subject: [PATCH 48/73] ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -> Profile smoothing using gaussian blur --- src/libslic3r/Slicing.cpp | 74 +++++++++++++++++++++++++++++++++- src/libslic3r/Slicing.hpp | 5 +++ src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/GLCanvas3D.cpp | 52 +++++++++++++++++++++++- src/slic3r/GUI/GLCanvas3D.hpp | 14 +++++++ src/slic3r/GUI/Plater.cpp | 3 ++ 6 files changed, 145 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index b7816fd04..0d3b3af38 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -252,6 +252,9 @@ std::vector layer_height_profile_adaptive( // 2) Generate layers using the algorithm of @platsch // loop until we have at least one layer and the max slice_z reaches the object height +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + double cusp_value = 0.2; +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE std::vector layer_height_profile; layer_height_profile.push_back(0.); @@ -264,7 +267,7 @@ std::vector layer_height_profile_adaptive( double height = slicing_params.first_object_layer_height; int current_facet = 0; while ((slice_z - height) <= slicing_params.object_print_z_height()) { - height = 999; + height = 999.0; // Slic3r::debugf "\n Slice layer: %d\n", $id; // determine next layer height double cusp_height = as.cusp_height((float)slice_z, cusp_value, current_facet); @@ -318,7 +321,7 @@ std::vector layer_height_profile_adaptive( layer_height_profile.push_back(height); } - coordf_t last = std::max(slicing_params.first_object_layer_height, layer_height_profile[layer_height_profile.size() - 2]); + double last = std::max(slicing_params.first_object_layer_height, layer_height_profile[layer_height_profile.size() - 2]); layer_height_profile.push_back(last); layer_height_profile.push_back(slicing_params.first_object_layer_height); layer_height_profile.push_back(slicing_params.object_print_z_height()); @@ -327,6 +330,73 @@ std::vector layer_height_profile_adaptive( return layer_height_profile; } +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING +std::vector smooth_height_profile(const std::vector& profile, const SlicingParameters& slicing_params, + unsigned int radius) +{ + auto gauss_blur = [] (const std::vector& profile, unsigned int radius) -> std::vector { + auto gauss_kernel = [] (unsigned int radius) -> std::vector { + unsigned int size = 2 * radius + 1; + std::vector ret; + ret.reserve(size); + + // Reworked from static inline int getGaussianKernelSize(float sigma) taken from opencv-4.1.2\modules\features2d\src\kaze\AKAZEFeatures.cpp + double sigma = 0.3 * (double)(radius - 1) + 0.8; + double two_sq_sigma = 2.0 * sigma * sigma; + double inv_root_two_pi_sq_sigma = 1.0 / ::sqrt(M_PI * two_sq_sigma); + + for (unsigned int i = 0; i < size; ++i) + { + double x = (double)i - (double)radius; + ret.push_back(inv_root_two_pi_sq_sigma * ::exp(-x * x / two_sq_sigma)); + } + + return ret; + }; + + std::vector ret; + size_t size = profile.size(); + ret.reserve(size); + std::vector kernel = gauss_kernel(radius); + + for (size_t i = 0; i < size; ++i) + { + unsigned int id = 0; + double value = 0.0; + for (int j = (int)(i - radius); j <= (int)(i + radius); ++j) + { + if ((0 <= j) && (j < size)) + value += kernel[id] * profile[j]; + + ++id; + } + ret.push_back(value); + } + + return ret; + }; + + std::vector ret = profile; + + std::vector heights; + size_t heights_size = ret.size() / 2; + heights.reserve(heights_size); + for (size_t i = 0; i < heights_size; ++i) + { + heights.push_back(ret[i * 2 + 1]); + } + + heights = gauss_blur(heights, std::max(radius, (unsigned int)1)); + + for (size_t i = 0; i < heights_size; ++i) + { + ret[i * 2 + 1] = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, heights[i]); + } + + return ret; +} +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING + void adjust_layer_height_profile( const SlicingParameters &slicing_params, std::vector &layer_height_profile, diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index e25cbd326..545857216 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -146,6 +146,11 @@ extern std::vector layer_height_profile_from_ranges( extern std::vector layer_height_profile_adaptive( const SlicingParameters& slicing_params, const ModelObject& object, float cusp_value); + +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING +extern std::vector smooth_height_profile(const std::vector& profile, const SlicingParameters& slicing_params, + unsigned int radius); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #else extern std::vector layer_height_profile_adaptive( const SlicingParameters &slicing_params, diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index f8db1b7b3..465f5dd33 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -44,5 +44,6 @@ // Enable adaptive layer height profile #define ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE (1 && ENABLE_2_2_0_ALPHA1) +#define ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING (1 && ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE) #endif // _technologies_h_ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 8ef0eaa13..d0cd1fbeb 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -130,7 +130,12 @@ GLCanvas3D::LayersEditing::LayersEditing() , m_object_max_z(0.f) , m_slicing_parameters(nullptr) , m_layer_height_profile_modified(false) +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE , m_adaptive_cusp(0.2f) +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING + , m_smooth_radius(5) +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE , state(Unknown) , band_width(2.0f) , strength(0.005f) @@ -227,9 +232,10 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const float canvas_w = (float)cnv_size.get_width(); float canvas_h = (float)cnv_size.get_height(); + const float scale_gl = wxGetApp().mainframe->scale_factor(); ImGuiWrapper& imgui = *wxGetApp().imgui(); - imgui.set_next_window_pos(canvas_w - THICKNESS_BAR_WIDTH, canvas_h, ImGuiCond_Always, 1.0f, 1.0f); + imgui.set_next_window_pos(canvas_w - scale_gl * THICKNESS_BAR_WIDTH, canvas_h, ImGuiCond_Always, 1.0f, 1.0f); imgui.set_next_window_bg_alpha(0.5f); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); @@ -271,12 +277,30 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, m_adaptive_cusp)); ImGui::SameLine(); + float text_align = ImGui::GetCursorPosX(); imgui.text(_(L("Cusp (mm)"))); ImGui::SameLine(); - ImGui::PushItemWidth(100.0f); + float widget_align = ImGui::GetCursorPosX(); + ImGui::PushItemWidth(120.0f); m_adaptive_cusp = std::min(m_adaptive_cusp, (float)m_slicing_parameters->max_layer_height); ImGui::SliderFloat("", &m_adaptive_cusp, 0.0f, (float)m_slicing_parameters->max_layer_height, "%.2f"); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING + ImGui::Separator(); + if (imgui.button(_(L("Smooth")))) + wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, m_smooth_radius)); + + ImGui::SameLine(); + ImGui::SetCursorPosX(text_align); + imgui.text(_(L("Radius"))); + ImGui::SameLine(); + ImGui::PushItemWidth(120.0f); + ImGui::SetCursorPosX(widget_align); + int radius = (int)m_smooth_radius; + if (ImGui::SliderInt("##1", &radius, 1, 10)) + m_smooth_radius = (unsigned int)radius; +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING + ImGui::Separator(); if (imgui.button(_(L("Reset")))) wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE)); @@ -590,6 +614,17 @@ void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas m_layers_texture.valid = false; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } + +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING +void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, unsigned int radius) +{ + m_layer_height_profile = smooth_height_profile(m_layer_height_profile, *m_slicing_parameters, radius); + const_cast(m_model_object)->layer_height_profile = m_layer_height_profile; + m_layers_texture.valid = false; + canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +} +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING + #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void GLCanvas3D::LayersEditing::generate_layer_height_texture() @@ -1219,6 +1254,9 @@ wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING +wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, Event); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE #if ENABLE_THUMBNAIL_GENERATOR @@ -1536,6 +1574,16 @@ void GLCanvas3D::adaptive_layer_height_profile(float cusp) m_layers_editing.state = LayersEditing::Completed; m_dirty = true; } + +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING +void GLCanvas3D::smooth_layer_height_profile(unsigned int radius) +{ + m_layers_editing.smooth_layer_height_profile(*this, radius); + m_layers_editing.state = LayersEditing::Completed; + m_dirty = true; +} +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING + #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool GLCanvas3D::is_reload_delayed() const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index d1369d086..d57a5c8d6 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -107,6 +107,9 @@ wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING +wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, Event); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class GLCanvas3D @@ -179,7 +182,12 @@ private: std::vector m_layer_height_profile; bool m_layer_height_profile_modified; +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE mutable float m_adaptive_cusp; +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING + mutable unsigned int m_smooth_radius; +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class LayersTexture { @@ -229,6 +237,9 @@ private: void reset_layer_height_profile(GLCanvas3D& canvas); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING + void smooth_layer_height_profile(GLCanvas3D& canvas, unsigned int radius); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static float get_cursor_z_relative(const GLCanvas3D& canvas); @@ -526,6 +537,9 @@ public: #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void reset_layer_height_profile(); void adaptive_layer_height_profile(float cusp); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING + void smooth_layer_height_profile(unsigned int radius); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool is_reload_delayed() const; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e2b15602c..3c93486d8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2092,6 +2092,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); }); view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); }); +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING + view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](Event& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); }); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // 3DScene/Toolbar: From 7baefdf79dea3bb4ab88cb5ea324354bed5eacde Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 18 Nov 2019 10:04:32 +0100 Subject: [PATCH 49/73] Fix for deps patching --- deps/deps-unix-common.cmake | 13 ++++++------- deps/deps-windows.cmake | 11 ++++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/deps/deps-unix-common.cmake b/deps/deps-unix-common.cmake index 74582f601..944d2c06d 100644 --- a/deps/deps-unix-common.cmake +++ b/deps/deps-unix-common.cmake @@ -61,8 +61,8 @@ ExternalProject_Add(dep_qhull -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local ${DEP_CMAKE_OPTS} - UPDATE_COMMAND "" - PATCH_COMMAND patch -p1 < ${CMAKE_CURRENT_SOURCE_DIR}/qhull-mods.patch + PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && + ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/qhull-mods.patch ) ExternalProject_Add(dep_blosc @@ -80,8 +80,8 @@ ExternalProject_Add(dep_blosc -DBUILD_TESTS=OFF -DBUILD_BENCHMARKS=OFF -DPREFER_EXTERNAL_ZLIB=ON - UPDATE_COMMAND "" - PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch + PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && + ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch ) ExternalProject_Add(dep_openexr @@ -96,7 +96,6 @@ ExternalProject_Add(dep_openexr -DPYILMBASE_ENABLE:BOOL=OFF -DOPENEXR_VIEWERS_ENABLE:BOOL=OFF -DOPENEXR_BUILD_UTILS:BOOL=OFF - UPDATE_COMMAND "" ) ExternalProject_Add(dep_openvdb @@ -116,6 +115,6 @@ ExternalProject_Add(dep_openvdb -DOPENVDB_CORE_STATIC=ON -DTBB_STATIC=ON -DOPENVDB_BUILD_VDB_PRINT=ON - UPDATE_COMMAND "" - PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch + PATCH_COMMAND PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && + ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch ) \ No newline at end of file diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index 4aae07d4a..3affbaefa 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -226,7 +226,8 @@ ExternalProject_Add(dep_qhull -DBUILD_SHARED_LIBS=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_DEBUG_POSTFIX=d - UPDATE_COMMAND "" + PATCH_COMMAND PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && + ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/qhull-mods.patch BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj INSTALL_COMMAND "" ) @@ -287,8 +288,8 @@ ExternalProject_Add(dep_blosc -DPREFER_EXTERNAL_ZLIB=ON -DBLOSC_IS_SUBPROJECT:BOOL=ON -DBLOSC_INSTALL:BOOL=ON - UPDATE_COMMAND "" - PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch + PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && + ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj INSTALL_COMMAND "" ) @@ -339,8 +340,8 @@ ExternalProject_Add(dep_openvdb -DTBB_STATIC=ON -DOPENVDB_BUILD_VDB_PRINT=ON BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj - UPDATE_COMMAND "" - PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch + PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && + ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch INSTALL_COMMAND "" ) From ab735bdc5445a77aa0fb69f22c4cd9565b382124 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 18 Nov 2019 09:35:26 +0100 Subject: [PATCH 50/73] Follow up: fixing patches on windows --- deps/blosc-mods.patch | 3 ++- deps/deps-windows.cmake | 5 +---- deps/openvdb-mods.patch | 3 ++- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/deps/blosc-mods.patch b/deps/blosc-mods.patch index 9a91b4974..9b1b9cb27 100644 --- a/deps/blosc-mods.patch +++ b/deps/blosc-mods.patch @@ -1,8 +1,9 @@ -From 5669891dfaaa4c814f3ec667ca6bf4e693aea978 Mon Sep 17 00:00:00 2001 +From 7cf6c014a36f1712efbdbe9bc52d2d4922b54673 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 30 Oct 2019 12:54:52 +0100 Subject: [PATCH] Blosc 1.17 fixes and cmake config script +Signed-off-by: tamasmeszaros --- CMakeLists.txt | 105 +++++++++++++++++----------------- blosc/CMakeLists.txt | 118 +++++++++------------------------------ diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index 3affbaefa..1b978bdde 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -226,8 +226,6 @@ ExternalProject_Add(dep_qhull -DBUILD_SHARED_LIBS=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_DEBUG_POSTFIX=d - PATCH_COMMAND PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && - ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/qhull-mods.patch BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj INSTALL_COMMAND "" ) @@ -311,7 +309,6 @@ ExternalProject_Add(dep_openexr -DPYILMBASE_ENABLE:BOOL=OFF -DOPENEXR_VIEWERS_ENABLE:BOOL=OFF -DOPENEXR_BUILD_UTILS:BOOL=OFF - UPDATE_COMMAND "" BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj INSTALL_COMMAND "" ) @@ -324,7 +321,7 @@ ExternalProject_Add(dep_openvdb #URL_HASH SHA256=dc337399dce8e1c9f21f20e97b1ce7e4933cb0a63bb3b8b734d8fcc464aa0c48 GIT_REPOSITORY https://github.com/AcademySoftwareFoundation/openvdb.git GIT_TAG aebaf8d95be5e57fd33949281ec357db4a576c2e #v6.2.1 - DEPENDS dep_blosc dep_openexr #dep_tbb dep_boost + DEPENDS dep_blosc dep_openexr dep_tbb dep_boost CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" CMAKE_ARGS diff --git a/deps/openvdb-mods.patch b/deps/openvdb-mods.patch index 60687b8d1..023cb5308 100644 --- a/deps/openvdb-mods.patch +++ b/deps/openvdb-mods.patch @@ -1,8 +1,9 @@ -From e48f4a835fe7cb391f9f90945472bd367fb4c4f1 Mon Sep 17 00:00:00 2001 +From dbe038fce8a15ddc9a5c83ec5156d7bc9e178015 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 16 Oct 2019 17:42:50 +0200 Subject: [PATCH] Build fixes for PrusaSlicer integration +Signed-off-by: tamasmeszaros --- CMakeLists.txt | 3 - cmake/FindBlosc.cmake | 218 --------------- From 68a9980a5e7fb25a411c5b4deed2c09c4c6bd4c8 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 18 Nov 2019 10:17:44 +0100 Subject: [PATCH 51/73] ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -> Small refactoring to improve performances --- src/libslic3r/Slicing.cpp | 16 +++++++++------- src/slic3r/GUI/GLCanvas3D.cpp | 2 ++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 0d3b3af38..9fb2e00f4 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -361,16 +361,15 @@ std::vector smooth_height_profile(const std::vector& profile, co for (size_t i = 0; i < size; ++i) { - unsigned int id = 0; - double value = 0.0; - for (int j = (int)(i - radius); j <= (int)(i + radius); ++j) + ret.push_back(0.0); + double& height = ret.back(); + int begin = (int)(i - radius); + int end = (int)(i + radius); + for (int j = begin; j <= end; ++j) { if ((0 <= j) && (j < size)) - value += kernel[id] * profile[j]; - - ++id; + height += kernel[j - begin] * profile[j]; } - ret.push_back(value); } return ret; @@ -381,13 +380,16 @@ std::vector smooth_height_profile(const std::vector& profile, co std::vector heights; size_t heights_size = ret.size() / 2; heights.reserve(heights_size); + // extract heights from profile for (size_t i = 0; i < heights_size; ++i) { heights.push_back(ret[i * 2 + 1]); } + // smooth heights heights = gauss_blur(heights, std::max(radius, (unsigned int)1)); + // put smoothed heights back into profile for (size_t i = 0; i < heights_size; ++i) { ret[i * 2 + 1] = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, heights[i]); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index d0cd1fbeb..d9746bd05 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -287,6 +287,7 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING ImGui::Separator(); + imgui.disabled_begin(m_layer_height_profile.size() < 10); if (imgui.button(_(L("Smooth")))) wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, m_smooth_radius)); @@ -299,6 +300,7 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const int radius = (int)m_smooth_radius; if (ImGui::SliderInt("##1", &radius, 1, 10)) m_smooth_radius = (unsigned int)radius; + imgui.disabled_end(); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING ImGui::Separator(); From 28dedd65f0bd2149f8af9c3280be9b2fb2f54ac4 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 18 Nov 2019 10:25:23 +0100 Subject: [PATCH 52/73] ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -> Code cleanup --- src/libslic3r/Slicing.cpp | 2 -- src/libslic3r/Slicing.hpp | 6 +++--- src/libslic3r/Technologies.hpp | 1 - src/slic3r/GUI/GLCanvas3D.cpp | 12 ------------ src/slic3r/GUI/GLCanvas3D.hpp | 8 -------- src/slic3r/GUI/Plater.cpp | 2 -- 6 files changed, 3 insertions(+), 28 deletions(-) diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 9fb2e00f4..42e721744 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -330,7 +330,6 @@ std::vector layer_height_profile_adaptive( return layer_height_profile; } -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING std::vector smooth_height_profile(const std::vector& profile, const SlicingParameters& slicing_params, unsigned int radius) { @@ -397,7 +396,6 @@ std::vector smooth_height_profile(const std::vector& profile, co return ret; } -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING void adjust_layer_height_profile( const SlicingParameters &slicing_params, diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index 545857216..8dc0b47e9 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -147,10 +147,10 @@ extern std::vector layer_height_profile_adaptive( const SlicingParameters& slicing_params, const ModelObject& object, float cusp_value); -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING -extern std::vector smooth_height_profile(const std::vector& profile, const SlicingParameters& slicing_params, +extern std::vector smooth_height_profile( + const std::vector& profile, + const SlicingParameters& slicing_params, unsigned int radius); -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #else extern std::vector layer_height_profile_adaptive( const SlicingParameters &slicing_params, diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 465f5dd33..f8db1b7b3 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -44,6 +44,5 @@ // Enable adaptive layer height profile #define ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE (1 && ENABLE_2_2_0_ALPHA1) -#define ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING (1 && ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE) #endif // _technologies_h_ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index d9746bd05..5c5fce5c6 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -132,9 +132,7 @@ GLCanvas3D::LayersEditing::LayersEditing() , m_layer_height_profile_modified(false) #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE , m_adaptive_cusp(0.2f) -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING , m_smooth_radius(5) -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE , state(Unknown) , band_width(2.0f) @@ -285,7 +283,6 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const m_adaptive_cusp = std::min(m_adaptive_cusp, (float)m_slicing_parameters->max_layer_height); ImGui::SliderFloat("", &m_adaptive_cusp, 0.0f, (float)m_slicing_parameters->max_layer_height, "%.2f"); -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING ImGui::Separator(); imgui.disabled_begin(m_layer_height_profile.size() < 10); if (imgui.button(_(L("Smooth")))) @@ -301,7 +298,6 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const if (ImGui::SliderInt("##1", &radius, 1, 10)) m_smooth_radius = (unsigned int)radius; imgui.disabled_end(); -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING ImGui::Separator(); if (imgui.button(_(L("Reset")))) @@ -617,7 +613,6 @@ void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, unsigned int radius) { m_layer_height_profile = smooth_height_profile(m_layer_height_profile, *m_slicing_parameters, radius); @@ -625,8 +620,6 @@ void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, m_layers_texture.valid = false; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING - #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void GLCanvas3D::LayersEditing::generate_layer_height_texture() @@ -1256,9 +1249,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event); -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, Event); -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE #if ENABLE_THUMBNAIL_GENERATOR @@ -1577,15 +1568,12 @@ void GLCanvas3D::adaptive_layer_height_profile(float cusp) m_dirty = true; } -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING void GLCanvas3D::smooth_layer_height_profile(unsigned int radius) { m_layers_editing.smooth_layer_height_profile(*this, radius); m_layers_editing.state = LayersEditing::Completed; m_dirty = true; } -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING - #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool GLCanvas3D::is_reload_delayed() const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index d57a5c8d6..4a1859d54 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -107,9 +107,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event); -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, Event); -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class GLCanvas3D @@ -184,9 +182,7 @@ private: #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE mutable float m_adaptive_cusp; -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING mutable unsigned int m_smooth_radius; -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class LayersTexture @@ -237,9 +233,7 @@ private: void reset_layer_height_profile(GLCanvas3D& canvas); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp); -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING void smooth_layer_height_profile(GLCanvas3D& canvas, unsigned int radius); -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static float get_cursor_z_relative(const GLCanvas3D& canvas); @@ -537,9 +531,7 @@ public: #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void reset_layer_height_profile(); void adaptive_layer_height_profile(float cusp); -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING void smooth_layer_height_profile(unsigned int radius); -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool is_reload_delayed() const; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 2750af7df..faf7a3d69 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2090,9 +2090,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); }); view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); }); -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](Event& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); }); -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE_SMOOTHING #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // 3DScene/Toolbar: From cfc927b3711701c68ad1e594e10db86e69f489ad Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 18 Nov 2019 10:59:58 +0100 Subject: [PATCH 53/73] Fix qhull download --- deps/deps-unix-common.cmake | 6 ++++-- deps/deps-windows.cmake | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/deps/deps-unix-common.cmake b/deps/deps-unix-common.cmake index 944d2c06d..d0305bd4d 100644 --- a/deps/deps-unix-common.cmake +++ b/deps/deps-unix-common.cmake @@ -55,8 +55,10 @@ find_package(Git REQUIRED) ExternalProject_Add(dep_qhull EXCLUDE_FROM_ALL 1 - URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" - URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 + #URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" + #URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 + GIT_REPOSITORY https://github.com/qhull/qhull.git + GIT_TAG v7.3.2 CMAKE_ARGS -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index 1b978bdde..cca88c308 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -218,8 +218,10 @@ find_package(Git REQUIRED) ExternalProject_Add(dep_qhull EXCLUDE_FROM_ALL 1 - URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" - URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 + #URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" + #URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 + GIT_REPOSITORY https://github.com/qhull/qhull.git + GIT_TAG v7.3.2 CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local From 3b8ee784008df955c8fc0ea4be7b1e935c0d7ce7 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 18 Nov 2019 11:05:25 +0100 Subject: [PATCH 54/73] Use the upstream qhull with the macos patch instead of patching manually --- deps/deps-unix-common.cmake | 4 +--- deps/deps-windows.cmake | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/deps/deps-unix-common.cmake b/deps/deps-unix-common.cmake index d0305bd4d..b2d82c950 100644 --- a/deps/deps-unix-common.cmake +++ b/deps/deps-unix-common.cmake @@ -58,13 +58,11 @@ ExternalProject_Add(dep_qhull #URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" #URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 GIT_REPOSITORY https://github.com/qhull/qhull.git - GIT_TAG v7.3.2 + GIT_TAG 7afedcc73666e46a9f1d74632412ebecf53b1b30 # v7.3.2 plus the mac build patch CMAKE_ARGS -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local ${DEP_CMAKE_OPTS} - PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && - ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/qhull-mods.patch ) ExternalProject_Add(dep_blosc diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index cca88c308..603f24931 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -221,7 +221,7 @@ ExternalProject_Add(dep_qhull #URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz" #URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0 GIT_REPOSITORY https://github.com/qhull/qhull.git - GIT_TAG v7.3.2 + GIT_TAG 7afedcc73666e46a9f1d74632412ebecf53b1b30 # v7.3.2 plus the mac build patch CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local From 54357f846f695d9879fb023e8cb8de7804ca0dca Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 18 Nov 2019 12:09:46 +0100 Subject: [PATCH 55/73] Change blosc patching "checkout -f" to "reset --hard" --- deps/deps-unix-common.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/deps-unix-common.cmake b/deps/deps-unix-common.cmake index b2d82c950..7491aafe1 100644 --- a/deps/deps-unix-common.cmake +++ b/deps/deps-unix-common.cmake @@ -80,7 +80,7 @@ ExternalProject_Add(dep_blosc -DBUILD_TESTS=OFF -DBUILD_BENCHMARKS=OFF -DPREFER_EXTERNAL_ZLIB=ON - PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && + PATCH_COMMAND ${GIT_EXECUTABLE} reset --hard && git clean -df && ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch ) From da44618074fdbe7771a234be29b23d462ec7e3c4 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 18 Nov 2019 14:32:41 +0100 Subject: [PATCH 56/73] ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -> Fixed adaptive layer height profile and layer height profile smoothing calculations --- src/libslic3r/Slicing.cpp | 85 ++++++++++++++++++++++------------- src/slic3r/GUI/GLCanvas3D.cpp | 2 - 2 files changed, 54 insertions(+), 33 deletions(-) diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 42e721744..66e339246 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -257,17 +257,22 @@ std::vector layer_height_profile_adaptive( #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE std::vector layer_height_profile; - layer_height_profile.push_back(0.); + layer_height_profile.push_back(0.0); layer_height_profile.push_back(slicing_params.first_object_layer_height); if (slicing_params.first_object_layer_height_fixed()) { layer_height_profile.push_back(slicing_params.first_object_layer_height); layer_height_profile.push_back(slicing_params.first_object_layer_height); } double slice_z = slicing_params.first_object_layer_height; - double height = slicing_params.first_object_layer_height; int current_facet = 0; +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + while (slice_z <= slicing_params.object_print_z_height()) { + double height = 999.0; +#else + double height = slicing_params.first_object_layer_height; while ((slice_z - height) <= slicing_params.object_print_z_height()) { height = 999.0; +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // Slic3r::debugf "\n Slice layer: %d\n", $id; // determine next layer height double cusp_height = as.cusp_height((float)slice_z, cusp_value, current_facet); @@ -317,15 +322,26 @@ std::vector layer_height_profile_adaptive( layer_height_profile.push_back(slice_z); layer_height_profile.push_back(height); slice_z += height; +#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE layer_height_profile.push_back(slice_z); layer_height_profile.push_back(height); +#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE } +#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + double z_gap = slicing_params.object_print_z_height() - layer_height_profile[layer_height_profile.size() - 2]; + if (z_gap > 0.0) + { + layer_height_profile.push_back(slicing_params.object_print_z_height()); + layer_height_profile.push_back(clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, z_gap)); + } +#else double last = std::max(slicing_params.first_object_layer_height, layer_height_profile[layer_height_profile.size() - 2]); layer_height_profile.push_back(last); layer_height_profile.push_back(slicing_params.first_object_layer_height); layer_height_profile.push_back(slicing_params.object_print_z_height()); layer_height_profile.push_back(slicing_params.first_object_layer_height); +#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE return layer_height_profile; } @@ -333,7 +349,7 @@ std::vector layer_height_profile_adaptive( std::vector smooth_height_profile(const std::vector& profile, const SlicingParameters& slicing_params, unsigned int radius) { - auto gauss_blur = [] (const std::vector& profile, unsigned int radius) -> std::vector { + auto gauss_blur = [&slicing_params](const std::vector& profile, unsigned int radius) -> std::vector { auto gauss_kernel = [] (unsigned int radius) -> std::vector { unsigned int size = 2 * radius + 1; std::vector ret; @@ -353,48 +369,55 @@ std::vector smooth_height_profile(const std::vector& profile, co return ret; }; + // skip first layer ? + size_t skip_count = slicing_params.first_object_layer_height_fixed() ? 4 : 0; + + // not enough data to smmoth + if ((int)profile.size() - (int)skip_count < 6) + return profile; + + std::vector kernel = gauss_kernel(radius); + int two_radius = 2 * (int)radius; + std::vector ret; size_t size = profile.size(); ret.reserve(size); - std::vector kernel = gauss_kernel(radius); - for (size_t i = 0; i < size; ++i) + // leave first layer untouched + for (size_t i = 0; i < skip_count; ++i) { + ret.push_back(profile[i]); + } + + // smooth the rest of the profile + double max_dz = (double)radius * slicing_params.layer_height; + for (size_t i = skip_count; i < size; i += 2) + { + double z = profile[i]; + ret.push_back(z); ret.push_back(0.0); double& height = ret.back(); - int begin = (int)(i - radius); - int end = (int)(i + radius); - for (int j = begin; j <= end; ++j) + int begin = std::max((int)i - two_radius, (int)skip_count); + int end = std::min((int)i + two_radius, (int)size - 2); + double kernel_total = 0.0; + for (int j = begin; j <= end; j += 2) { - if ((0 <= j) && (j < size)) - height += kernel[j - begin] * profile[j]; + int kernel_id = radius + (j - (int)i) / 2; + double dz = std::abs(z - profile[j]); + if (dz * slicing_params.layer_height <= max_dz) + { + height += kernel[kernel_id] * profile[j + 1]; + kernel_total += kernel[kernel_id]; + } } + + height = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, (kernel_total != 0.0) ? height /= kernel_total : profile[i + 1]); } return ret; }; - std::vector ret = profile; - - std::vector heights; - size_t heights_size = ret.size() / 2; - heights.reserve(heights_size); - // extract heights from profile - for (size_t i = 0; i < heights_size; ++i) - { - heights.push_back(ret[i * 2 + 1]); - } - - // smooth heights - heights = gauss_blur(heights, std::max(radius, (unsigned int)1)); - - // put smoothed heights back into profile - for (size_t i = 0; i < heights_size; ++i) - { - ret[i * 2 + 1] = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, heights[i]); - } - - return ret; + return gauss_blur(profile, std::max(radius, (unsigned int)1)); } void adjust_layer_height_profile( diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 5c5fce5c6..7827a9566 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -284,7 +284,6 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const ImGui::SliderFloat("", &m_adaptive_cusp, 0.0f, (float)m_slicing_parameters->max_layer_height, "%.2f"); ImGui::Separator(); - imgui.disabled_begin(m_layer_height_profile.size() < 10); if (imgui.button(_(L("Smooth")))) wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, m_smooth_radius)); @@ -297,7 +296,6 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const int radius = (int)m_smooth_radius; if (ImGui::SliderInt("##1", &radius, 1, 10)) m_smooth_radius = (unsigned int)radius; - imgui.disabled_end(); ImGui::Separator(); if (imgui.button(_(L("Reset")))) From b8c09d7a34583343301c6fd697232d661399a128 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 18 Nov 2019 15:31:55 +0100 Subject: [PATCH 57/73] Content of file 90-3dconnexion.rules formatted as in: https://github.com/libusb/hidapi/blob/master/udev/99-hid.rules --- resources/udev/90-3dconnexion.rules | 124 +++++++++++++++++++--------- 1 file changed, 84 insertions(+), 40 deletions(-) diff --git a/resources/udev/90-3dconnexion.rules b/resources/udev/90-3dconnexion.rules index 670d91790..df010f105 100644 --- a/resources/udev/90-3dconnexion.rules +++ b/resources/udev/90-3dconnexion.rules @@ -1,45 +1,89 @@ # See src/slic3r/GUI/Mouse3DController.cpp for the list of devices # Logitech vendor devices -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c603", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c605", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c606", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c621", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c623", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c625", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c626", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c627", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c628", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c629", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62b", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62e", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62f", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c631", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c632", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c633", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c635", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c636", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c640", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c652", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c603", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c605", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c606", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c621", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c623", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c625", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c626", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c627", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c628", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c629", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62b", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62e", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62f", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c631", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c632", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c633", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c635", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c636", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c640", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c652", MODE="0666" # 3D Connexion vendor devices -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c603", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c605", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c606", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c621", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c623", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c625", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c626", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c627", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c628", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c629", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62b", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62e", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62f", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c631", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c632", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c633", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c635", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c636", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c640", MODE="0666" -SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c652", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c603", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c605", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c606", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c621", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c623", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c625", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c626", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c627", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c628", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c629", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62b", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62e", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62f", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c631", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c632", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c633", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c635", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c636", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c640", MODE="0666" +KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c652", MODE="0666" + +## Logitech vendor devices +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c603", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c605", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c606", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c621", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c623", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c625", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c626", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c627", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c628", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c629", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62b", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62e", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62f", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c631", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c632", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c633", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c635", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c636", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c640", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c652", MODE="0666" +# +## 3D Connexion vendor devices +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c603", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c605", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c606", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c621", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c623", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c625", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c626", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c627", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c628", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c629", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62b", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62e", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62f", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c631", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c632", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c633", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c635", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c636", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c640", MODE="0666" +#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c652", MODE="0666" From 4fceff6ae37889aee40f6a21d8da8e8cdc318f05 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 18 Nov 2019 16:07:39 +0100 Subject: [PATCH 58/73] Cleanup of file 90-3dconnexion.rules --- resources/udev/90-3dconnexion.rules | 44 ----------------------------- 1 file changed, 44 deletions(-) diff --git a/resources/udev/90-3dconnexion.rules b/resources/udev/90-3dconnexion.rules index df010f105..04d581498 100644 --- a/resources/udev/90-3dconnexion.rules +++ b/resources/udev/90-3dconnexion.rules @@ -43,47 +43,3 @@ KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c635", MODE="0666 KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c636", MODE="0666" KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c640", MODE="0666" KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c652", MODE="0666" - -## Logitech vendor devices -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c603", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c605", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c606", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c621", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c623", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c625", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c626", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c627", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c628", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c629", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62b", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62e", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62f", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c631", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c632", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c633", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c635", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c636", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c640", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c652", MODE="0666" -# -## 3D Connexion vendor devices -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c603", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c605", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c606", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c621", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c623", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c625", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c626", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c627", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c628", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c629", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62b", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62e", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62f", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c631", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c632", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c633", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c635", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c636", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c640", MODE="0666" -#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c652", MODE="0666" From 25ab9e8d5d2f9e061b453528a7cf80a0ac66e9d2 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 19 Nov 2019 11:43:20 +0100 Subject: [PATCH 59/73] Fixed normals transformation in functions stl_transform() --- src/admesh/stl.h | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/admesh/stl.h b/src/admesh/stl.h index fa0edec2b..9224b0459 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -184,10 +184,21 @@ extern void stl_mirror_xz(stl_file *stl); extern void stl_get_size(stl_file *stl); +// the following function is not used +/* template extern void stl_transform(stl_file *stl, T *trafo3x4) { - for (uint32_t i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) { + Eigen::Matrix trafo3x3; + for (int i = 0; i < 3; ++i) + { + for (int j = 0; j < 3; ++j) + { + trafo3x3(i, j) = (i * 4) + j; + } + } + Eigen::Matrix r = trafo3x3.inverse().transpose(); + for (uint32_t i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) { stl_facet &face = stl->facet_start[i_face]; for (int i_vertex = 0; i_vertex < 3; ++ i_vertex) { stl_vertex &v_dst = face.vertex[i_vertex]; @@ -196,21 +207,18 @@ extern void stl_transform(stl_file *stl, T *trafo3x4) v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7]); v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11]); } - stl_vertex &v_dst = face.normal; - stl_vertex v_src = v_dst; - v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2)); - v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2)); - v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2)); - } + face.normal = (r * face.normal.template cast()).template cast().eval(); + } stl_get_size(stl); } +*/ template inline void stl_transform(stl_file *stl, const Eigen::Transform& t) { - const Eigen::Matrix r = t.matrix().template block<3, 3>(0, 0); - for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { + const Eigen::Matrix r = t.matrix().template block<3, 3>(0, 0).inverse().transpose(); + for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { stl_facet &f = stl->facet_start[i]; for (size_t j = 0; j < 3; ++j) f.vertex[j] = (t * f.vertex[j].template cast()).template cast().eval(); @@ -223,12 +231,13 @@ inline void stl_transform(stl_file *stl, const Eigen::Transform inline void stl_transform(stl_file *stl, const Eigen::Matrix& m) { - for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { + const Eigen::Matrix r = m.inverse().transpose(); + for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { stl_facet &f = stl->facet_start[i]; for (size_t j = 0; j < 3; ++j) f.vertex[j] = (m * f.vertex[j].template cast()).template cast().eval(); - f.normal = (m * f.normal.template cast()).template cast().eval(); - } + f.normal = (r * f.normal.template cast()).template cast().eval(); + } stl_get_size(stl); } From 3b084c50cdb3c0f4e52b3a320374db2807a4929a Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 19 Nov 2019 14:44:42 +0100 Subject: [PATCH 60/73] Revert to older texture for mini printer printbed --- resources/icons/bed/mini.svg | 145 +++++++++++++---------------------- 1 file changed, 53 insertions(+), 92 deletions(-) diff --git a/resources/icons/bed/mini.svg b/resources/icons/bed/mini.svg index 1b9476ef8..93c3437bd 100644 --- a/resources/icons/bed/mini.svg +++ b/resources/icons/bed/mini.svg @@ -1,109 +1,70 @@ - - bed_texture_denser - - - - - - - - - - + + MINI_bed_texture + + + + + + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 79d516ca7f9b14c3083c1048937448c4d76a926f Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 19 Nov 2019 14:58:27 +0100 Subject: [PATCH 61/73] ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -> Smoothing algorithm modified to give more weight close to height limits --- src/libslic3r/Slicing.cpp | 27 ++++++++++++++++----------- src/libslic3r/SlicingAdaptive.cpp | 4 ++++ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 66e339246..1badd9b14 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -346,8 +346,7 @@ std::vector layer_height_profile_adaptive( return layer_height_profile; } -std::vector smooth_height_profile(const std::vector& profile, const SlicingParameters& slicing_params, - unsigned int radius) +std::vector smooth_height_profile(const std::vector& profile, const SlicingParameters& slicing_params, unsigned int radius) { auto gauss_blur = [&slicing_params](const std::vector& profile, unsigned int radius) -> std::vector { auto gauss_kernel = [] (unsigned int radius) -> std::vector { @@ -390,28 +389,34 @@ std::vector smooth_height_profile(const std::vector& profile, co } // smooth the rest of the profile - double max_dz = (double)radius * slicing_params.layer_height; + double med_h = 0.5 * (slicing_params.min_layer_height + slicing_params.max_layer_height); + double half_delta_h = 0.5 * (slicing_params.max_layer_height - slicing_params.min_layer_height); + double inv_half_delta_h = (half_delta_h > 0.0) ? 1.0 / half_delta_h : 1.0; + + double max_dz_band = (double)radius * slicing_params.layer_height; for (size_t i = skip_count; i < size; i += 2) { - double z = profile[i]; - ret.push_back(z); + double zi = profile[i]; + ret.push_back(zi); ret.push_back(0.0); double& height = ret.back(); int begin = std::max((int)i - two_radius, (int)skip_count); int end = std::min((int)i + two_radius, (int)size - 2); - double kernel_total = 0.0; + double weight_total = 0.0; for (int j = begin; j <= end; j += 2) { int kernel_id = radius + (j - (int)i) / 2; - double dz = std::abs(z - profile[j]); - if (dz * slicing_params.layer_height <= max_dz) + double dz = std::abs(zi - profile[j]); + if (dz * slicing_params.layer_height <= max_dz_band) { - height += kernel[kernel_id] * profile[j + 1]; - kernel_total += kernel[kernel_id]; + double dh = std::abs(profile[j + 1] - med_h); + double weight = kernel[kernel_id] * dh * inv_half_delta_h; + height += weight * profile[j + 1]; + weight_total += weight; } } - height = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, (kernel_total != 0.0) ? height /= kernel_total : profile[i + 1]); + height = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, (weight_total != 0.0) ? height /= weight_total : profile[i + 1]); } return ret; diff --git a/src/libslic3r/SlicingAdaptive.cpp b/src/libslic3r/SlicingAdaptive.cpp index e64012553..bc02a89c1 100644 --- a/src/libslic3r/SlicingAdaptive.cpp +++ b/src/libslic3r/SlicingAdaptive.cpp @@ -37,6 +37,10 @@ void SlicingAdaptive::prepare() m_mesh = m_object->raw_mesh(); const ModelInstance* first_instance = m_object->instances.front(); m_mesh.transform(first_instance->get_matrix(), first_instance->is_left_handed()); + for (stl_facet& facet : m_mesh.stl.facet_start) + { + facet.normal.normalize(); + } // 1) Collect faces from mesh. m_faces.reserve(m_mesh.stl.stats.number_of_facets); From 4982e177a26ccfdd93b875da7bb6fa635c56fe3c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 19 Nov 2019 15:39:49 +0100 Subject: [PATCH 62/73] ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -> Attempt to fix imgui dialog position on Mac --- src/slic3r/GUI/GLCanvas3D.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7827a9566..4721901f4 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -230,7 +230,11 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const float canvas_w = (float)cnv_size.get_width(); float canvas_h = (float)cnv_size.get_height(); +#if ENABLE_RETINA_GL + const float scale_gl = m_retina_helper->get_scale_factor(); +#else const float scale_gl = wxGetApp().mainframe->scale_factor(); +#endif // ENABLE_RETINA_GL ImGuiWrapper& imgui = *wxGetApp().imgui(); imgui.set_next_window_pos(canvas_w - scale_gl * THICKNESS_BAR_WIDTH, canvas_h, ImGuiCond_Always, 1.0f, 1.0f); From 955439b3baf8ed03fa541e11325ec98a2083e061 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 20 Nov 2019 10:57:56 +0100 Subject: [PATCH 63/73] Fixed build on Mac --- src/slic3r/GUI/GLCanvas3D.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 4721901f4..818c2ae63 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -230,14 +230,8 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const float canvas_w = (float)cnv_size.get_width(); float canvas_h = (float)cnv_size.get_height(); -#if ENABLE_RETINA_GL - const float scale_gl = m_retina_helper->get_scale_factor(); -#else - const float scale_gl = wxGetApp().mainframe->scale_factor(); -#endif // ENABLE_RETINA_GL - ImGuiWrapper& imgui = *wxGetApp().imgui(); - imgui.set_next_window_pos(canvas_w - scale_gl * THICKNESS_BAR_WIDTH, canvas_h, ImGuiCond_Always, 1.0f, 1.0f); + imgui.set_next_window_pos(canvas_w - imgui.get_style_scaling() * THICKNESS_BAR_WIDTH, canvas_h, ImGuiCond_Always, 1.0f, 1.0f); imgui.set_next_window_bg_alpha(0.5f); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); From aea32ffe7272186ca20e30c3edea8ff92c1048b7 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 20 Nov 2019 14:06:30 +0100 Subject: [PATCH 64/73] ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -> Added 'keep min' option to smoothing algorithm --- src/libslic3r/Slicing.cpp | 26 +++++++++++++++----------- src/libslic3r/Slicing.hpp | 14 +++++++++++--- src/slic3r/GUI/GLCanvas3D.cpp | 24 +++++++++++++++--------- src/slic3r/GUI/GLCanvas3D.hpp | 10 ++++++---- src/slic3r/GUI/Plater.cpp | 2 +- 5 files changed, 48 insertions(+), 28 deletions(-) diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 1badd9b14..81e9bf0c5 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -346,9 +346,9 @@ std::vector layer_height_profile_adaptive( return layer_height_profile; } -std::vector smooth_height_profile(const std::vector& profile, const SlicingParameters& slicing_params, unsigned int radius) +std::vector smooth_height_profile(const std::vector& profile, const SlicingParameters& slicing_params, const HeightProfileSmoothingParams& smoothing_params) { - auto gauss_blur = [&slicing_params](const std::vector& profile, unsigned int radius) -> std::vector { + auto gauss_blur = [&slicing_params](const std::vector& profile, const HeightProfileSmoothingParams& smoothing_params) -> std::vector { auto gauss_kernel = [] (unsigned int radius) -> std::vector { unsigned int size = 2 * radius + 1; std::vector ret; @@ -374,7 +374,8 @@ std::vector smooth_height_profile(const std::vector& profile, co // not enough data to smmoth if ((int)profile.size() - (int)skip_count < 6) return profile; - + + unsigned int radius = std::max(smoothing_params.radius, (unsigned int)1); std::vector kernel = gauss_kernel(radius); int two_radius = 2 * (int)radius; @@ -388,15 +389,16 @@ std::vector smooth_height_profile(const std::vector& profile, co ret.push_back(profile[i]); } - // smooth the rest of the profile - double med_h = 0.5 * (slicing_params.min_layer_height + slicing_params.max_layer_height); - double half_delta_h = 0.5 * (slicing_params.max_layer_height - slicing_params.min_layer_height); - double inv_half_delta_h = (half_delta_h > 0.0) ? 1.0 / half_delta_h : 1.0; + // smooth the rest of the profile by biasing a gaussian blur + // the bias moves the smoothed profile closer to the min_layer_height + double delta_h = slicing_params.max_layer_height - slicing_params.min_layer_height; + double inv_delta_h = (delta_h != 0.0) ? 1.0 / delta_h : 1.0; double max_dz_band = (double)radius * slicing_params.layer_height; for (size_t i = skip_count; i < size; i += 2) { double zi = profile[i]; + double hi = profile[i + 1]; ret.push_back(zi); ret.push_back(0.0); double& height = ret.back(); @@ -409,20 +411,22 @@ std::vector smooth_height_profile(const std::vector& profile, co double dz = std::abs(zi - profile[j]); if (dz * slicing_params.layer_height <= max_dz_band) { - double dh = std::abs(profile[j + 1] - med_h); - double weight = kernel[kernel_id] * dh * inv_half_delta_h; + double dh = std::abs(slicing_params.max_layer_height - profile[j + 1]); + double weight = kernel[kernel_id] * sqrt(dh * inv_delta_h); height += weight * profile[j + 1]; weight_total += weight; } } - height = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, (weight_total != 0.0) ? height /= weight_total : profile[i + 1]); + height = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, (weight_total != 0.0) ? height /= weight_total : hi); + if (smoothing_params.keep_min) + height = std::min(height, hi); } return ret; }; - return gauss_blur(profile, std::max(radius, (unsigned int)1)); + return gauss_blur(profile, smoothing_params); } void adjust_layer_height_profile( diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index 8dc0b47e9..03ef7e67d 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -147,10 +147,18 @@ extern std::vector layer_height_profile_adaptive( const SlicingParameters& slicing_params, const ModelObject& object, float cusp_value); +struct HeightProfileSmoothingParams +{ + unsigned int radius; + bool keep_min; + + HeightProfileSmoothingParams() : radius(5), keep_min(false) {} + HeightProfileSmoothingParams(unsigned int radius, bool keep_min) : radius(radius), keep_min(keep_min) {} +}; + extern std::vector smooth_height_profile( - const std::vector& profile, - const SlicingParameters& slicing_params, - unsigned int radius); + const std::vector& profile, const SlicingParameters& slicing_params, + const HeightProfileSmoothingParams& smoothing_params); #else extern std::vector layer_height_profile_adaptive( const SlicingParameters &slicing_params, diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 818c2ae63..a459a3ec8 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -132,7 +132,6 @@ GLCanvas3D::LayersEditing::LayersEditing() , m_layer_height_profile_modified(false) #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE , m_adaptive_cusp(0.2f) - , m_smooth_radius(5) #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE , state(Unknown) , band_width(2.0f) @@ -283,7 +282,7 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const ImGui::Separator(); if (imgui.button(_(L("Smooth")))) - wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, m_smooth_radius)); + wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), HeightProfileSmoothEvent(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, m_smooth_params )); ImGui::SameLine(); ImGui::SetCursorPosX(text_align); @@ -291,9 +290,16 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const ImGui::SameLine(); ImGui::PushItemWidth(120.0f); ImGui::SetCursorPosX(widget_align); - int radius = (int)m_smooth_radius; + int radius = (int)m_smooth_params.radius; if (ImGui::SliderInt("##1", &radius, 1, 10)) - m_smooth_radius = (unsigned int)radius; + m_smooth_params.radius = (unsigned int)radius; + + ImGui::SetCursorPosX(text_align); + imgui.text(_(L("Keep min"))); + ImGui::SameLine(); + ImGui::PushItemWidth(120.0f); + ImGui::SetCursorPosX(widget_align); + imgui.checkbox("##2", m_smooth_params.keep_min); ImGui::Separator(); if (imgui.button(_(L("Reset")))) @@ -609,9 +615,9 @@ void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } -void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, unsigned int radius) +void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_params) { - m_layer_height_profile = smooth_height_profile(m_layer_height_profile, *m_slicing_parameters, radius); + m_layer_height_profile = smooth_height_profile(m_layer_height_profile, *m_slicing_parameters, smoothing_params); const_cast(m_model_object)->layer_height_profile = m_layer_height_profile; m_layers_texture.valid = false; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); @@ -1245,7 +1251,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event); -wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, Event); +wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE #if ENABLE_THUMBNAIL_GENERATOR @@ -1564,9 +1570,9 @@ void GLCanvas3D::adaptive_layer_height_profile(float cusp) m_dirty = true; } -void GLCanvas3D::smooth_layer_height_profile(unsigned int radius) +void GLCanvas3D::smooth_layer_height_profile(const HeightProfileSmoothingParams& smoothing_params) { - m_layers_editing.smooth_layer_height_profile(*this, radius); + m_layers_editing.smooth_layer_height_profile(*this, smoothing_params); m_layers_editing.state = LayersEditing::Completed; m_dirty = true; } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 4a1859d54..ad35f3909 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -81,6 +81,8 @@ template using Vec2dsEvent = ArrayEvent; using Vec3dEvent = Event; template using Vec3dsEvent = ArrayEvent; +using HeightProfileSmoothEvent = Event; + wxDECLARE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent); @@ -107,7 +109,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event); -wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, Event); +wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class GLCanvas3D @@ -182,7 +184,7 @@ private: #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE mutable float m_adaptive_cusp; - mutable unsigned int m_smooth_radius; + mutable HeightProfileSmoothingParams m_smooth_params; #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class LayersTexture @@ -233,7 +235,7 @@ private: void reset_layer_height_profile(GLCanvas3D& canvas); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp); - void smooth_layer_height_profile(GLCanvas3D& canvas, unsigned int radius); + void smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_paramsn); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static float get_cursor_z_relative(const GLCanvas3D& canvas); @@ -531,7 +533,7 @@ public: #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void reset_layer_height_profile(); void adaptive_layer_height_profile(float cusp); - void smooth_layer_height_profile(unsigned int radius); + void smooth_layer_height_profile(const HeightProfileSmoothingParams& smoothing_params); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE bool is_reload_delayed() const; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index faf7a3d69..563bb5133 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2090,7 +2090,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); }); view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); }); - view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](Event& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); }); + view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](HeightProfileSmoothEvent& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); }); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // 3DScene/Toolbar: From 6df506e4824ee26b68379cb4760c1e22a509385e Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 22 Nov 2019 12:39:03 +0100 Subject: [PATCH 65/73] ENABLE_THUMBNAIL_GENERATOR -> Thumbnails generated using a callback function --- src/libslic3r/GCode.cpp | 113 ++++++++++---------- src/libslic3r/GCode.hpp | 10 +- src/libslic3r/GCode/ThumbnailData.hpp | 4 + src/libslic3r/Print.cpp | 4 +- src/libslic3r/Print.hpp | 8 +- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 19 ++-- src/slic3r/GUI/BackgroundSlicingProcess.hpp | 9 +- src/slic3r/GUI/Plater.cpp | 77 ++++--------- 8 files changed, 108 insertions(+), 136 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 56a94b28c..28bcdf50c 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -6,9 +6,6 @@ #include "Geometry.hpp" #include "GCode/PrintExtents.hpp" #include "GCode/WipeTower.hpp" -#if ENABLE_THUMBNAIL_GENERATOR -#include "GCode/ThumbnailData.hpp" -#endif // ENABLE_THUMBNAIL_GENERATOR #include "ShortestPath.hpp" #include "Utils.hpp" @@ -695,7 +692,7 @@ std::vector>> GCode::collec } #if ENABLE_THUMBNAIL_GENERATOR -void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, const std::vector* thumbnail_data) +void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb) #else void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_data) #endif // ENABLE_THUMBNAIL_GENERATOR @@ -725,7 +722,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ try { m_placeholder_parser_failed_templates.clear(); #if ENABLE_THUMBNAIL_GENERATOR - this->_do_export(*print, file, thumbnail_data); + this->_do_export(*print, file, thumbnail_cb); #else this->_do_export(*print, file); #endif // ENABLE_THUMBNAIL_GENERATOR @@ -793,9 +790,9 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ } #if ENABLE_THUMBNAIL_GENERATOR -void GCode::_do_export(Print& print, FILE* file, const std::vector* thumbnail_data) +void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb) #else -void GCode::_do_export(Print &print, FILE *file) +void GCode::_do_export(Print& print, FILE* file) #endif // ENABLE_THUMBNAIL_GENERATOR { PROFILE_FUNC(); @@ -812,46 +809,46 @@ void GCode::_do_export(Print &print, FILE *file) // shall be adjusted as well to produce a G-code block compatible with the particular firmware flavor. if (print.config().gcode_flavor.value == gcfMarlin) { m_normal_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values[0]); - m_normal_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values[0]); - m_normal_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values[0]); - m_normal_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values[0]); - m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values[0]); - m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values[0]); - m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values[0]); - m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values[0]); - m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values[0]); - m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values[0]); - m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values[0]); - m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values[0]); - m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values[0]); - m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values[0]); - m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values[0]); - m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values[0]); + m_normal_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values[0]); + m_normal_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values[0]); + m_normal_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values[0]); + m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values[0]); + m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values[0]); + m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values[0]); + m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values[0]); + m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values[0]); + m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values[0]); + m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values[0]); + m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values[0]); + m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values[0]); + m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values[0]); + m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values[0]); + m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values[0]); if (m_silent_time_estimator_enabled) { m_silent_time_estimator.reset(); m_silent_time_estimator.set_dialect(print.config().gcode_flavor); - /* "Stealth mode" values can be just a copy of "normal mode" values + /* "Stealth mode" values can be just a copy of "normal mode" values * (when they aren't input for a printer preset). * Thus, use back value from values, instead of second one, which could be absent */ - m_silent_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values.back()); - m_silent_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values.back()); - m_silent_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values.back()); - m_silent_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values.back()); - m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values.back()); - m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values.back()); - m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values.back()); - m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values.back()); - m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values.back()); - m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values.back()); - m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values.back()); - m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values.back()); - m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values.back()); - m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values.back()); - m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values.back()); - m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values.back()); + m_silent_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values.back()); + m_silent_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values.back()); + m_silent_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values.back()); + m_silent_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values.back()); + m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values.back()); + m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values.back()); + m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values.back()); + m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values.back()); + m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values.back()); + m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values.back()); + m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values.back()); + m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values.back()); + m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values.back()); + m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values.back()); + m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values.back()); + m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values.back()); if (print.config().single_extruder_multi_material) { // As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they // are considered to be active for the single extruder multi-material printers only. @@ -909,7 +906,8 @@ void GCode::_do_export(Print &print, FILE *file) std::sort(zs.begin(), zs.end()); m_layer_count += (unsigned int)(object->copies().size() * (std::unique(zs.begin(), zs.end()) - zs.begin())); } - } else { + } + else { // Print all objects with the same print_z together. std::vector zs; for (auto object : print.objects()) { @@ -927,7 +925,7 @@ void GCode::_do_export(Print &print, FILE *file) m_enable_cooling_markers = true; this->apply_print_config(print.config()); this->set_extruders(print.extruders()); - + // Initialize colorprint. m_colorprint_heights = cast(print.config().colorprint_heights.values); @@ -936,31 +934,31 @@ void GCode::_do_export(Print &print, FILE *file) // get the minimum cross-section used in the print std::vector mm3_per_mm; for (auto object : print.objects()) { - for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < object->region_volumes.size(); ++region_id) { const PrintRegion* region = print.regions()[region_id]; for (auto layer : object->layers()) { const LayerRegion* layerm = layer->regions()[region_id]; - if (region->config().get_abs_value("perimeter_speed" ) == 0 || - region->config().get_abs_value("small_perimeter_speed" ) == 0 || - region->config().get_abs_value("external_perimeter_speed" ) == 0 || - region->config().get_abs_value("bridge_speed" ) == 0) + if (region->config().get_abs_value("perimeter_speed") == 0 || + region->config().get_abs_value("small_perimeter_speed") == 0 || + region->config().get_abs_value("external_perimeter_speed") == 0 || + region->config().get_abs_value("bridge_speed") == 0) mm3_per_mm.push_back(layerm->perimeters.min_mm3_per_mm()); - if (region->config().get_abs_value("infill_speed" ) == 0 || - region->config().get_abs_value("solid_infill_speed" ) == 0 || - region->config().get_abs_value("top_solid_infill_speed" ) == 0 || - region->config().get_abs_value("bridge_speed" ) == 0) + if (region->config().get_abs_value("infill_speed") == 0 || + region->config().get_abs_value("solid_infill_speed") == 0 || + region->config().get_abs_value("top_solid_infill_speed") == 0 || + region->config().get_abs_value("bridge_speed") == 0) mm3_per_mm.push_back(layerm->fills.min_mm3_per_mm()); } } - if (object->config().get_abs_value("support_material_speed" ) == 0 || - object->config().get_abs_value("support_material_interface_speed" ) == 0) + if (object->config().get_abs_value("support_material_speed") == 0 || + object->config().get_abs_value("support_material_interface_speed") == 0) for (auto layer : object->support_layers()) mm3_per_mm.push_back(layer->support_fills.min_mm3_per_mm()); } print.throw_if_canceled(); // filter out 0-width segments mm3_per_mm.erase(std::remove_if(mm3_per_mm.begin(), mm3_per_mm.end(), [](double v) { return v < 0.000001; }), mm3_per_mm.end()); - if (! mm3_per_mm.empty()) { + if (!mm3_per_mm.empty()) { // In order to honor max_print_speed we need to find a target volumetric // speed that we can use throughout the print. So we define this target // volumetric speed as the volumetric speed produced by printing the @@ -973,7 +971,7 @@ void GCode::_do_export(Print &print, FILE *file) } } print.throw_if_canceled(); - + m_cooling_buffer = make_unique(*this); if (print.config().spiral_vase.value) m_spiral_vase = make_unique(print.config()); @@ -991,11 +989,12 @@ void GCode::_do_export(Print &print, FILE *file) #if ENABLE_THUMBNAIL_GENERATOR // Write thumbnails using base64 encoding - if (thumbnail_data != nullptr) + if (thumbnail_cb != nullptr) { const size_t max_row_length = 78; - - for (const ThumbnailData& data : *thumbnail_data) + ThumbnailsList thumbnails; + thumbnail_cb(thumbnails, print.full_print_config().option("thumbnails")->values, true, true, false); + for (const ThumbnailData& data : thumbnails) { if (data.is_valid()) { diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 19ec5be3c..40794986a 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -17,6 +17,9 @@ #include "GCodeTimeEstimator.hpp" #include "EdgeGrid.hpp" #include "GCode/Analyzer.hpp" +#if ENABLE_THUMBNAIL_GENERATOR +#include "GCode/ThumbnailData.hpp" +#endif // ENABLE_THUMBNAIL_GENERATOR #include #include @@ -30,9 +33,6 @@ namespace Slic3r { // Forward declarations. class GCode; class GCodePreviewData; -#if ENABLE_THUMBNAIL_GENERATOR -struct ThumbnailData; -#endif // ENABLE_THUMBNAIL_GENERATOR class AvoidCrossingPerimeters { public: @@ -167,7 +167,7 @@ public: // throws std::runtime_exception on error, // throws CanceledException through print->throw_if_canceled(). #if ENABLE_THUMBNAIL_GENERATOR - void do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, const std::vector* thumbnail_data = nullptr); + void do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); #else void do_export(Print *print, const char *path, GCodePreviewData *preview_data = nullptr); #endif // ENABLE_THUMBNAIL_GENERATOR @@ -199,7 +199,7 @@ public: protected: #if ENABLE_THUMBNAIL_GENERATOR - void _do_export(Print& print, FILE* file, const std::vector* thumbnail_data); + void _do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb); #else void _do_export(Print &print, FILE *file); #endif //ENABLE_THUMBNAIL_GENERATOR diff --git a/src/libslic3r/GCode/ThumbnailData.hpp b/src/libslic3r/GCode/ThumbnailData.hpp index 9823ffd31..efe875e15 100644 --- a/src/libslic3r/GCode/ThumbnailData.hpp +++ b/src/libslic3r/GCode/ThumbnailData.hpp @@ -4,6 +4,7 @@ #if ENABLE_THUMBNAIL_GENERATOR #include +#include "libslic3r/Point.hpp" namespace Slic3r { @@ -20,6 +21,9 @@ struct ThumbnailData bool is_valid() const; }; +typedef std::vector ThumbnailsList; +typedef std::function ThumbnailsGeneratorCallback; + } // namespace Slic3r #endif // ENABLE_THUMBNAIL_GENERATOR diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 78a09c511..8b79f5367 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1538,7 +1538,7 @@ void Print::process() // write error into the G-code, cannot execute post-processing scripts). // It is up to the caller to show an error message. #if ENABLE_THUMBNAIL_GENERATOR -std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, const std::vector* thumbnail_data) +std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb) #else std::string Print::export_gcode(const std::string &path_template, GCodePreviewData *preview_data) #endif // ENABLE_THUMBNAIL_GENERATOR @@ -1559,7 +1559,7 @@ std::string Print::export_gcode(const std::string &path_template, GCodePreviewDa // The following line may die for multiple reasons. GCode gcode; #if ENABLE_THUMBNAIL_GENERATOR - gcode.do_export(this, path.c_str(), preview_data, thumbnail_data); + gcode.do_export(this, path.c_str(), preview_data, thumbnail_cb); #else gcode.do_export(this, path.c_str(), preview_data); #endif // ENABLE_THUMBNAIL_GENERATOR diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 4fcd67166..1649f3c04 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -11,6 +11,9 @@ #include "Slicing.hpp" #include "GCode/ToolOrdering.hpp" #include "GCode/WipeTower.hpp" +#if ENABLE_THUMBNAIL_GENERATOR +#include "GCode/ThumbnailData.hpp" +#endif // ENABLE_THUMBNAIL_GENERATOR namespace Slic3r { @@ -19,9 +22,6 @@ class PrintObject; class ModelObject; class GCode; class GCodePreviewData; -#if ENABLE_THUMBNAIL_GENERATOR -struct ThumbnailData; -#endif // ENABLE_THUMBNAIL_GENERATOR // Print step IDs for keeping track of the print state. enum PrintStep { @@ -311,7 +311,7 @@ public: // Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file. // If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r). #if ENABLE_THUMBNAIL_GENERATOR - std::string export_gcode(const std::string& path_template, GCodePreviewData* preview_data, const std::vector* thumbnail_data = nullptr); + std::string export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); #else std::string export_gcode(const std::string &path_template, GCodePreviewData *preview_data); #endif // ENABLE_THUMBNAIL_GENERATOR diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 5ab65f340..570841a45 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -20,9 +20,6 @@ #include "libslic3r/Utils.hpp" #include "libslic3r/GCode/PostProcessor.hpp" #include "libslic3r/GCode/PreviewData.hpp" -#if ENABLE_THUMBNAIL_GENERATOR -#include "libslic3r/GCode/ThumbnailData.hpp" -#endif // ENABLE_THUMBNAIL_GENERATOR #include "libslic3r/libslic3r.h" #include @@ -91,7 +88,7 @@ void BackgroundSlicingProcess::process_fff() m_print->process(); wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_slicing_completed_id)); #if ENABLE_THUMBNAIL_GENERATOR - m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_data); + m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_cb); #else m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data); #endif // ENABLE_THUMBNAIL_GENERATOR @@ -139,9 +136,12 @@ void BackgroundSlicingProcess::process_sla() m_sla_print->export_raster(zipper); #if ENABLE_THUMBNAIL_GENERATOR - if (m_thumbnail_data != nullptr) + if (m_thumbnail_cb != nullptr) { - for (const ThumbnailData& data : *m_thumbnail_data) + ThumbnailsList thumbnails; + m_thumbnail_cb(thumbnails, current_print()->full_print_config().option("thumbnails")->values, true, true, false); +// m_thumbnail_cb(thumbnails, current_print()->full_print_config().option("thumbnails")->values, true, false, false); // renders also supports and pad + for (const ThumbnailData& data : thumbnails) { if (data.is_valid()) write_thumbnail(zipper, data); @@ -461,9 +461,12 @@ void BackgroundSlicingProcess::prepare_upload() Zipper zipper{source_path.string()}; m_sla_print->export_raster(zipper, m_upload_job.upload_data.upload_path.string()); #if ENABLE_THUMBNAIL_GENERATOR - if (m_thumbnail_data != nullptr) + if (m_thumbnail_cb != nullptr) { - for (const ThumbnailData& data : *m_thumbnail_data) + ThumbnailsList thumbnails; + m_thumbnail_cb(thumbnails, current_print()->full_print_config().option("thumbnails")->values, true, true, false); +// m_thumbnail_cb(thumbnails, current_print()->full_print_config().option("thumbnails")->values, true, false, false); // renders also supports and pad + for (const ThumbnailData& data : thumbnails) { if (data.is_valid()) write_thumbnail(zipper, data); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index a603d52ac..984686e35 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -17,9 +17,6 @@ namespace Slic3r { class DynamicPrintConfig; class GCodePreviewData; -#if ENABLE_THUMBNAIL_GENERATOR -struct ThumbnailData; -#endif // ENABLE_THUMBNAIL_GENERATOR class Model; class SLAPrint; @@ -53,7 +50,7 @@ public: void set_sla_print(SLAPrint *print) { m_sla_print = print; } void set_gcode_preview_data(GCodePreviewData *gpd) { m_gcode_preview_data = gpd; } #if ENABLE_THUMBNAIL_GENERATOR - void set_thumbnail_data(const std::vector* data) { m_thumbnail_data = data; } + void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; } #endif // ENABLE_THUMBNAIL_GENERATOR // The following wxCommandEvent will be sent to the UI thread / Platter window, when the slicing is finished @@ -159,8 +156,8 @@ private: // Data structure, to which the G-code export writes its annotations. GCodePreviewData *m_gcode_preview_data = nullptr; #if ENABLE_THUMBNAIL_GENERATOR - // Data structures, used to write thumbnails into gcode. - const std::vector* m_thumbnail_data = nullptr; + // Callback function, used to write thumbnails into gcode. + ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr; #endif // ENABLE_THUMBNAIL_GENERATOR // Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID. std::string m_temp_output_path; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f04e1e17f..6f1c776fd 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1387,9 +1387,6 @@ struct Plater::priv Slic3r::Model model; PrinterTechnology printer_technology = ptFFF; Slic3r::GCodePreviewData gcode_preview_data; -#if ENABLE_THUMBNAIL_GENERATOR - std::vector thumbnail_data; -#endif // ENABLE_THUMBNAIL_GENERATOR // GUI elements wxSizer* panel_sizer{ nullptr }; @@ -1946,6 +1943,7 @@ struct Plater::priv #if ENABLE_THUMBNAIL_GENERATOR void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool transparent_background); + void generate_thumbnails(ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background); #endif // ENABLE_THUMBNAIL_GENERATOR void msw_rescale_object_menu(); @@ -2016,7 +2014,15 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) background_process.set_sla_print(&sla_print); background_process.set_gcode_preview_data(&gcode_preview_data); #if ENABLE_THUMBNAIL_GENERATOR - background_process.set_thumbnail_data(&thumbnail_data); + background_process.set_thumbnail_cb([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background) + { + std::packaged_task task([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background) { + generate_thumbnails(thumbnails, sizes, printable_only, parts_only, transparent_background); + }); + std::future result = task.get_future(); + wxTheApp->CallAfter([&]() { task(thumbnails, sizes, printable_only, parts_only, transparent_background); }); + result.wait(); + }); #endif // ENABLE_THUMBNAIL_GENERATOR background_process.set_slicing_completed_event(EVT_SLICING_COMPLETED); background_process.set_finished_event(EVT_PROCESS_COMPLETED); @@ -3062,37 +3068,6 @@ bool Plater::priv::restart_background_process(unsigned int state) ( ((state & UPDATE_BACKGROUND_PROCESS_FORCE_RESTART) != 0 && ! this->background_process.finished()) || (state & UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT) != 0 || (state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 ) ) { -#if ENABLE_THUMBNAIL_GENERATOR - if (((state & UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT) == 0) && - (this->background_process.state() != BackgroundSlicingProcess::STATE_RUNNING)) - { - // update thumbnail data - const std::vector &thumbnail_sizes = this->background_process.current_print()->full_print_config().option("thumbnails")->values; - if (this->printer_technology == ptFFF) - { - // for ptFFF we need to generate the thumbnails before the export of gcode starts - this->thumbnail_data.clear(); - for (const Vec2d &sized : thumbnail_sizes) - { - this->thumbnail_data.push_back(ThumbnailData()); - Point size(sized); // round to ints - generate_thumbnail(this->thumbnail_data.back(), size.x(), size.y(), true, true, false); - } - } - else if (this->printer_technology == ptSLA) - { - // for ptSLA generate thumbnails without supports and pad (not yet calculated) - // to render also supports and pad see on_slicing_update() - this->thumbnail_data.clear(); - for (const Vec2d &sized : thumbnail_sizes) - { - this->thumbnail_data.push_back(ThumbnailData()); - Point size(sized); // round to ints - generate_thumbnail(this->thumbnail_data.back(), size.x(), size.y(), true, true, false); - } - } - } -#endif // ENABLE_THUMBNAIL_GENERATOR // The print is valid and it can be started. if (this->background_process.start()) { this->statusbar()->set_cancel_callback([this]() { @@ -3430,25 +3405,6 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) } else if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_PREVIEW) { // Update the SLA preview. Only called if not RELOAD_SLA_SUPPORT_POINTS, as the block above will refresh the preview anyways. this->preview->reload_print(); - - // uncomment the following lines if you want to render into the thumbnail also supports and pad for SLA printer -/* -#if ENABLE_THUMBNAIL_GENERATOR - // update thumbnail data - // for ptSLA generate the thumbnail after supports and pad have been calculated to have them rendered - if ((this->printer_technology == ptSLA) && (evt.status.percent == -3)) - { - const std::vector& thumbnail_sizes = this->background_process.current_print()->full_print_config().option("thumbnails")->values; - this->thumbnail_data.clear(); - for (const Vec2d &sized : thumbnail_sizes) - { - this->thumbnail_data.push_back(ThumbnailData()); - Point size(sized); // round to ints - generate_thumbnail(this->thumbnail_data.back(), size.x(), size.y(), true, false, false); - } - } -#endif // ENABLE_THUMBNAIL_GENERATOR -*/ } } @@ -3679,6 +3635,19 @@ void Plater::priv::generate_thumbnail(ThumbnailData& data, unsigned int w, unsig { view3D->get_canvas3d()->render_thumbnail(data, w, h, printable_only, parts_only, transparent_background); } + +void Plater::priv::generate_thumbnails(ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background) +{ + thumbnails.clear(); + for (const Vec2d& size : sizes) + { + thumbnails.push_back(ThumbnailData()); + Point isize(size); // round to ints + generate_thumbnail(thumbnails.back(), isize.x(), isize.y(), printable_only, parts_only, transparent_background); + if (!thumbnails.back().is_valid()) + thumbnails.pop_back(); + } +} #endif // ENABLE_THUMBNAIL_GENERATOR void Plater::priv::msw_rescale_object_menu() From 4cff569b6239ac66565d303ddc4f6c6cd26d8769 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 22 Nov 2019 15:33:20 +0100 Subject: [PATCH 66/73] Improvements of infill path planning: Implementation of 2-opt pairwise exchange iterative improvement algorithm with an extension to a chain of segments, where the chain of segments may get flipped during the exchange operation. The 2-opt exchange algorithm may be quite slow. --- src/libslic3r/ShortestPath.cpp | 883 +++++++++++++++++++++++++++++- tests/libslic3r/test_geometry.cpp | 36 ++ 2 files changed, 903 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp index b38655e68..0ebd6c173 100644 --- a/src/libslic3r/ShortestPath.cpp +++ b/src/libslic3r/ShortestPath.cpp @@ -389,6 +389,585 @@ std::vector> chain_segments_greedy_constrained_reversals return out; } +template +void update_end_point_in_queue(QueueType &queue, const KDTreeType &kdtree, ChainsType &chains, std::vector &end_points, EndPointType &end_point, size_t first_point_idx, const EndPointType *first_point) +{ + // Updating an end point or a 2nd from an end point. + size_t this_idx = end_point.index(end_points); + // If this segment is not the starting segment, then this end point or the opposite is unconnected. + assert(first_point_idx == this_idx || first_point_idx == (this_idx ^ 1) || end_point.chain_id == 0 || end_point.opposite(end_points).chain_id == 0); + end_point.edge_candidate = nullptr; + if (first_point_idx == this_idx || (end_point.chain_id > 0 && first_point_idx == (this_idx ^ 1))) + { + // One may never flip the 1st edge, don't try it again. + if (! end_point.heap_idx_invalid()) + queue.remove(end_point.heap_idx); + } + else + { + // Update edge_candidate and distance. + size_t chain1a = end_point.chain_id; + size_t chain1b = end_points[this_idx ^ 1].chain_id; + size_t this_chain = chains.equivalent(std::max(chain1a, chain1b)); + // Find the closest point to this end_point, which lies on a different extrusion path (filtered by the filter lambda). + size_t next_idx = find_closest_point(kdtree, end_point.pos, [&end_points, &chains, this_idx, first_point_idx, first_point, this_chain](size_t idx) { + assert(end_points[this_idx].edge_candidate == nullptr); + // Either this end of the edge or the other end of the edge is not yet connected. + assert((end_points[this_idx ].chain_id == 0 && end_points[this_idx ].edge_out == nullptr) || + (end_points[this_idx ^ 1].chain_id == 0 && end_points[this_idx ^ 1].edge_out == nullptr)); + if ((idx ^ this_idx) <= 1 || idx == first_point_idx) + // Points of the same segment shall not be connected. + // Don't connect to the first point, we must not flip the 1st edge. + return false; + size_t chain2a = end_points[idx].chain_id; + size_t chain2b = end_points[idx ^ 1].chain_id; + if (chain2a > 0 && chain2b > 0) + // Only unconnected end point or a point next to an unconnected end point may be connected to. + // Ideally those would be removed from the KD tree, but the update is difficult. + return false; + assert(chain2a == 0 || chain2b == 0); + size_t chain2 = chains.equivalent(std::max(chain2a, chain2b)); + if (this_chain == chain2) + // Don't connect back to the same chain, don't create a loop. + return this_chain == 0; + // Don't connect to a segment requiring flipping if the segment starts or ends with the first point. + if (chain2a > 0) { + // Chain requires flipping. + assert(chain2b == 0); + auto &chain = chains.chain(chain2); + if (chain.begin == first_point || chain.end == first_point) + return false; + } + // Everything is all right, try to connect. + return true; + }); + assert(next_idx < end_points.size()); + assert(chains.equivalent(end_points[next_idx].chain_id) != chains.equivalent(end_points[next_idx ^ 1].chain_id) || end_points[next_idx].chain_id == 0); + end_point.edge_candidate = &end_points[next_idx]; + end_point.distance_out = (end_points[next_idx].pos - end_point.pos).norm(); + if (end_point.chain_id > 0) + end_point.distance_out += chains.chain_flip_penalty(this_chain); + if (end_points[next_idx].chain_id > 0) + // The candidate chain is flipped. + end_point.distance_out += chains.chain_flip_penalty(end_points[next_idx].chain_id); + // Update position of this end point in the queue based on the distance calculated at the line above. + if (end_point.heap_idx_invalid()) + queue.push(&end_point); + else + queue.update(end_point.heap_idx); + } +} + +template +std::vector> chain_segments_greedy_constrained_reversals2_(SegmentEndPointFunc end_point_func, CouldReverseFunc could_reverse_func, size_t num_segments, const PointType *start_near) +{ + std::vector> out; + + if (num_segments == 0) { + // Nothing to do. + } + else if (num_segments == 1) + { + // Just sort the end points so that the first point visited is closest to start_near. + out.emplace_back(0, start_near != nullptr && + (end_point_func(0, true) - *start_near).template cast().squaredNorm() < (end_point_func(0, false) - *start_near).template cast().squaredNorm()); + } + else + { + // End points of segments for the KD tree closest point search. + // A single end point is inserted into the search structure for loops, two end points are entered for open paths. + struct EndPoint { + EndPoint(const Vec2d &pos) : pos(pos) {} + Vec2d pos; + + // Candidate for a new connection link. + EndPoint *edge_candidate = nullptr; + // Distance to the next end point following the link. + // Zero value -> start of the final path. + double distance_out = std::numeric_limits::max(); + + size_t heap_idx = std::numeric_limits::max(); + bool heap_idx_invalid() const { return this->heap_idx == std::numeric_limits::max(); } + + // Identifier of the chain, to which this end point belongs. Zero means unassigned. + size_t chain_id = 0; + // Double linked chain of segment end points in current path. + EndPoint *edge_out = nullptr; + + size_t index(std::vector &endpoints) const { return this - endpoints.data(); } + // Opposite end point of the same segment. + EndPoint& opposite(std::vector &endpoints) { return endpoints[(this - endpoints.data()) ^ 1]; } + const EndPoint& opposite(const std::vector &endpoints) const { return endpoints[(this - endpoints.data()) ^ 1]; } + }; + + std::vector end_points; + end_points.reserve(num_segments * 2); + for (size_t i = 0; i < num_segments; ++ i) { + end_points.emplace_back(end_point_func(i, true ).template cast()); + end_points.emplace_back(end_point_func(i, false).template cast()); + } + + // Construct the closest point KD tree over end points of segments. + auto coordinate_fn = [&end_points](size_t idx, size_t dimension) -> double { return end_points[idx].pos[dimension]; }; + KDTreeIndirect<2, double, decltype(coordinate_fn)> kdtree(coordinate_fn, end_points.size()); + + // Chained segments with their sum of connection lengths. + // The chain supports flipping all the segments, connecting the segments at the opposite ends. + // (this is a very useful path optimization for infill lines). + struct Chain { + size_t num_segments = 0; + double cost = 0.; + double cost_flipped = 0.; + EndPoint *begin = nullptr; + EndPoint *end = nullptr; + size_t equivalent_with = 0; + + // Flipping the chain has a time complexity of O(n). + void flip(std::vector &endpoints) + { + assert(this->num_segments > 1); + assert(this->begin->edge_out == nullptr); + assert(this->end ->edge_out == nullptr); + assert(this->begin->opposite(endpoints).edge_out != nullptr); + assert(this->end ->opposite(endpoints).edge_out != nullptr); + // Start of the current segment processed. + EndPoint *ept = this->begin; + // Previous end point to connect the other side of ept to. + EndPoint *ept_prev = nullptr; + do { + EndPoint *ept_end = &ept->opposite(endpoints); + EndPoint *ept_next = ept_end->edge_out; + assert(ept_next == nullptr || ept_next->edge_out == ept_end); + // Connect to the preceding segment. + ept_end->edge_out = ept_prev; + if (ept_prev != nullptr) + ept_prev->edge_out = ept_end; + ept_prev = ept; + ept = ept_next; + } while (ept != nullptr); + ept_prev->edge_out = nullptr; + // Swap the costs. + std::swap(this->cost, this->cost_flipped); + // Swap the ends. + EndPoint *new_begin = &this->begin->opposite(endpoints); + EndPoint *new_end = &this->end->opposite(endpoints); + std::swap(this->begin->chain_id, new_begin->chain_id); + std::swap(this->end ->chain_id, new_end ->chain_id); + this->begin = new_begin; + this->end = new_end; + assert(this->begin->edge_out == nullptr); + assert(this->end ->edge_out == nullptr); + assert(this->begin->opposite(endpoints).edge_out != nullptr); + assert(this->end ->opposite(endpoints).edge_out != nullptr); + } + + double flip_penalty() const { return this->cost_flipped - this->cost; } + }; + + // Helper to detect loops in already connected paths and to accomodate flipping of chains. + // + // Unique chain IDs are assigned to paths. If paths are connected, end points will not have their chain IDs updated, but the chain IDs + // will remember an "equivalent" chain ID, which is the lowest ID of all the IDs in the path, and the lowest ID is equivalent to itself. + // Chain IDs are indexed starting with 1. + // + // Chains remember their lengths and their lengths when each segment of the chain is flipped. + class Chains { + public: + // Zero'th chain ID is invalid. + Chains(size_t reserve) { + m_chains.reserve(reserve / 2); + // Indexing starts with 1. + m_chains.emplace_back(); + } + + // Generate next equivalence class. + size_t next_id() { + m_chains.emplace_back(); + m_chains.back().equivalent_with = ++ m_last_chain_id; + return m_last_chain_id; + } + + // Get equivalence class for chain ID, update the "equivalent_with" along the equivalence path. + size_t equivalent(size_t chain_id) { + if (chain_id != 0) { + for (size_t last = chain_id;;) { + size_t lower = m_chains[last].equivalent_with; + if (lower == last) { + m_chains[chain_id].equivalent_with = lower; + chain_id = lower; + break; + } + last = lower; + } + } + return chain_id; + } + + // Return a lowest chain ID of the two input chains. + // Produce a new chain ID of both chain IDs are zero. + size_t merge(size_t chain_id1, size_t chain_id2) { + if (chain_id1 == 0) + return (chain_id2 == 0) ? this->next_id() : chain_id2; + if (chain_id2 == 0) + return chain_id1; + assert(m_chains[chain_id1].equivalent_with == chain_id1); + assert(m_chains[chain_id2].equivalent_with == chain_id2); + size_t chain_id = std::min(chain_id1, chain_id2); + m_chains[chain_id1].equivalent_with = chain_id; + m_chains[chain_id2].equivalent_with = chain_id; + return chain_id; + } + + Chain& chain(size_t chain_id) { return m_chains[chain_id]; } + const Chain& chain(size_t chain_id) const { return m_chains[chain_id]; } + + double chain_flip_penalty(size_t chain_id) { + chain_id = this->equivalent(chain_id); + return m_chains[chain_id].flip_penalty(); + } + +#ifndef NDEBUG + bool validate() + { + // Validate that the segments merged chain IDs make up a directed acyclic graph + // with edges oriented towards the lower chain ID, therefore all ending up + // in the lowest chain ID of all of them. + assert(m_last_chain_id >= 0); + assert(m_last_chain_id + 1 == m_chains.size()); + for (size_t i = 0; i < m_chains.size(); ++ i) { + for (size_t last = i;;) { + size_t lower = m_chains[last].equivalent_with; + assert(lower <= last); + if (lower == last) + break; + last = lower; + } + } + return true; + } +#endif /* NDEBUG */ + + private: + std::vector m_chains; + // Unique chain ID assigned to chains of end points of segments. + size_t m_last_chain_id = 0; + } chains(num_segments); + + // Find the first end point closest to start_near. + EndPoint *first_point = nullptr; + size_t first_point_idx = std::numeric_limits::max(); + if (start_near != nullptr) { + size_t idx = find_closest_point(kdtree, start_near->template cast()); + assert(idx < end_points.size()); + first_point = &end_points[idx]; + first_point->distance_out = 0.; + first_point->chain_id = chains.next_id(); + Chain &chain = chains.chain(first_point->chain_id); + chain.begin = first_point; + chain.end = &first_point->opposite(end_points); + first_point_idx = idx; + } + EndPoint *initial_point = first_point; + EndPoint *last_point = nullptr; + + // Assign the closest point and distance to the end points. + for (EndPoint &end_point : end_points) { + assert(end_point.edge_candidate == nullptr); + if (&end_point != first_point) { + size_t this_idx = end_point.index(end_points); + // Find the closest point to this end_point, which lies on a different extrusion path (filtered by the lambda). + // Ignore the starting point as the starting point is considered to be occupied, no end point coud connect to it. + size_t next_idx = find_closest_point(kdtree, end_point.pos, + [this_idx, first_point_idx](size_t idx){ return idx != first_point_idx && (idx ^ this_idx) > 1; }); + assert(next_idx < end_points.size()); + EndPoint &end_point2 = end_points[next_idx]; + end_point.edge_candidate = &end_point2; + end_point.distance_out = (end_point2.pos - end_point.pos).norm(); + } + } + + // Initialize a heap of end points sorted by the lowest distance to the next valid point of a path. + auto queue = make_mutable_priority_queue( + [](EndPoint *ep, size_t idx){ ep->heap_idx = idx; }, + [](EndPoint *l, EndPoint *r){ return l->distance_out < r->distance_out; }); + queue.reserve(end_points.size() * 2); + for (EndPoint &ep : end_points) + if (first_point != &ep) + queue.push(&ep); + +#ifndef NDEBUG + auto validate_graph_and_queue = [&chains, &end_points, &queue, first_point]() -> bool { + assert(chains.validate()); + for (EndPoint &ep : end_points) { + if (ep.heap_idx < queue.size()) { + // End point is on the heap. + assert(*(queue.cbegin() + ep.heap_idx) == &ep); + // One side or the other of the segment is not yet connected. + assert(ep.chain_id == 0 || ep.opposite(end_points).chain_id == 0); + } else { + // End point is NOT on the heap, therefore it must part of the output path. + assert(ep.heap_idx_invalid()); + assert(ep.chain_id != 0); + if (&ep == first_point) { + assert(ep.edge_out == nullptr); + } else { + assert(ep.edge_out != nullptr); + // Detect loops. + for (EndPoint *pt = &ep; pt != nullptr;) { + // Out of queue. It is a final point. + EndPoint *pt_other = &pt->opposite(end_points); + if (pt_other->heap_idx < queue.size()) { + // The other side of this segment is undecided yet. + // assert(pt_other->edge_out == nullptr); + break; + } + pt = pt_other->edge_out; + } + } + } + } + for (EndPoint *ep : queue) + // Points in the queue or the opposites of the same segment are not connected yet. + assert(ep->chain_id == 0 || ep->opposite(end_points).chain_id == 0); + return true; + }; +#endif /* NDEBUG */ + + // Chain the end points: find (num_segments - 1) shortest links not forming bifurcations or loops. + assert(num_segments >= 2); +#ifndef NDEBUG + double distance_taken_last = 0.; +#endif /* NDEBUG */ + // Some links stored onto the priority queue are being invalidated during the calculation and they are not + // updated immediately. If such a situation is detected for an end point pulled from the priority queue, + // the end point is being updated and re-inserted into the priority queue. Therefore the number of iterations + // required is higher than expected (it would be the number of links, num_segments - 1). + // The limit here may not be necessary, but it guards us against an endless loop if something goes wrong. + size_t num_iter = num_segments * 16; + for (size_t num_connections_to_end = num_segments - 1; num_iter > 0; -- num_iter) { + assert(validate_graph_and_queue()); + // Take the first end point, for which the link points to the currently closest valid neighbor. + EndPoint *end_point1 = queue.top(); + assert(end_point1 != first_point); + EndPoint *end_point1_other = &end_point1->opposite(end_points); + // true if end_point1 is not the end of its chain, but the 2nd point. When connecting to the 2nd point, this chain needs + // to be flipped first. + bool chain1_flip = end_point1->chain_id > 0; + // Either this point at the queue is not connected, or it is the 2nd point of a chain. + // If connecting to a 2nd point of a chain, the 1st point shall not yet be connected and this chain will need + // to be flipped. + assert( chain1_flip || (end_point1->chain_id == 0 && end_point1->edge_out == nullptr)); + assert(! chain1_flip || (end_point1_other->chain_id == 0 && end_point1_other->edge_out == nullptr)); + assert(end_point1->edge_candidate != nullptr); +#ifndef NDEBUG + // Each edge added shall be longer than the previous one taken. + //assert(end_point1->distance_out > distance_taken_last - SCALED_EPSILON); + if (end_point1->distance_out < distance_taken_last - SCALED_EPSILON) { +// printf("Warning: taking shorter length than previously is suspicious\n"); + } + distance_taken_last = end_point1->distance_out; +#endif /* NDEBUG */ + // Take the closest end point to the first end point, + EndPoint *end_point2 = end_point1->edge_candidate; + EndPoint *end_point2_other = &end_point2->opposite(end_points); + bool chain2_flip = end_point2->chain_id > 0; + // Is the link from end_point1 to end_point2 still valid? If yes, the link may be taken. Otherwise the link needs to be refreshed. + bool valid = true; + size_t end_point1_chain_id = 0; + size_t end_point2_chain_id = 0; + if (end_point2->chain_id > 0 && end_point2_other->chain_id > 0) { + // The other side is part of the output path. Don't connect to end_point2, update end_point1 and try another one. + valid = false; + } else { + // End points of the opposite ends of the segments. + end_point1_chain_id = chains.equivalent((chain1_flip ? end_point1 : end_point1_other)->chain_id); + end_point2_chain_id = chains.equivalent((chain2_flip ? end_point2 : end_point2_other)->chain_id); + if (end_point1_chain_id == end_point2_chain_id && end_point1_chain_id != 0) + // This edge forms a loop. Update end_point1 and try another one. + valid = false; + else { + // Verify whether end_point1.distance_out still matches the current state of the two end points to be connected and their chains. + // Namely, the other chain may have been flipped in the meantime. + double dist = (end_point2->pos - end_point1->pos).norm(); + if (chain1_flip) + dist += chains.chain_flip_penalty(end_point1_chain_id); + if (chain2_flip) + dist += chains.chain_flip_penalty(end_point2_chain_id); + if (std::abs(dist - end_point1->distance_out) > SCALED_EPSILON) + // The distance changed due to flipping of one of the chains. Refresh this end point in the queue. + valid = false; + } + if (valid && first_point != nullptr) { + // Verify that a chain starting or ending with the first_point does not get flipped. + if (chain1_flip) { + Chain &chain = chains.chain(end_point1_chain_id); + if (chain.begin == first_point || chain.end == first_point) + valid = false; + } + if (valid && chain2_flip) { + Chain &chain = chains.chain(end_point2_chain_id); + if (chain.begin == first_point || chain.end == first_point) + valid = false; + } + } + } + if (valid) { + // Remove the first and second point from the queue. + queue.pop(); + queue.remove(end_point2->heap_idx); + assert(end_point1->edge_candidate == end_point2); + end_point1->edge_candidate = nullptr; + Chain *chain1 = (end_point1_chain_id == 0) ? nullptr : &chains.chain(end_point1_chain_id); + Chain *chain2 = (end_point2_chain_id == 0) ? nullptr : &chains.chain(end_point2_chain_id); + assert(chain1 == nullptr || (chain1_flip ? (chain1->begin == end_point1_other || chain1->end == end_point1_other) : (chain1->begin == end_point1 || chain1->end == end_point1))); + assert(chain2 == nullptr || (chain2_flip ? (chain2->begin == end_point2_other || chain2->end == end_point2_other) : (chain2->begin == end_point2 || chain2->end == end_point2))); + if (chain1_flip) + chain1->flip(end_points); + if (chain2_flip) + chain2->flip(end_points); + assert(chain1 == nullptr || chain1->begin == end_point1 || chain1->end == end_point1); + assert(chain2 == nullptr || chain2->begin == end_point2 || chain2->end == end_point2); + size_t chain_id = chains.merge(end_point1_chain_id, end_point2_chain_id); + Chain &chain = chains.chain(chain_id); + { + Chain chain_dst; + chain_dst.begin = (chain1 == nullptr) ? end_point1_other : (chain1->begin == end_point1) ? chain1->end : chain1->begin; + chain_dst.end = (chain2 == nullptr) ? end_point2_other : (chain2->begin == end_point2) ? chain2->end : chain2->begin; + chain_dst.cost = (chain1 == 0 ? 0. : chain1->cost) + (chain2 == 0 ? 0. : chain2->cost) + (end_point2->pos - end_point1->pos).norm(); + chain_dst.cost_flipped = (chain1 == 0 ? 0. : chain1->cost_flipped) + (chain2 == 0 ? 0. : chain2->cost_flipped) + (end_point2_other->pos - end_point1_other->pos).norm(); + chain_dst.num_segments = (chain1 == 0 ? 1 : chain1->num_segments) + (chain2 == 0 ? 1 : chain2->num_segments); + chain_dst.equivalent_with = chain_id; + chain = chain_dst; + } + if (chain.begin != end_point1_other && ! end_point1_other->heap_idx_invalid()) + queue.remove(end_point1_other->heap_idx); + if (chain.end != end_point2_other && ! end_point2_other->heap_idx_invalid()) + queue.remove(end_point2_other->heap_idx); + end_point1->edge_out = end_point2; + end_point2->edge_out = end_point1; + end_point1->chain_id = chain_id; + end_point2->chain_id = chain_id; + end_point1_other->chain_id = chain_id; + end_point2_other->chain_id = chain_id; + if (chain.begin != first_point) + chain.begin->chain_id = 0; + if (chain.end != first_point) + chain.end->chain_id = 0; + if (-- num_connections_to_end == 0) { + assert(validate_graph_and_queue()); + // Last iteration. There shall be exactly one or two end points waiting to be connected. + assert(queue.size() <= ((first_point == nullptr) ? 4 : 2)); + if (first_point == nullptr) { + // Find the first remaining end point. + do { + first_point = queue.top(); + queue.pop(); + } while (first_point->edge_out != nullptr); + assert(first_point->edge_out == nullptr); + } + // Find the first remaining end point. + do { + last_point = queue.top(); + queue.pop(); + } while (last_point->edge_out != nullptr); + assert(last_point->edge_out == nullptr); +#ifndef NDEBUG + while (! queue.empty()) { + assert(queue.top()->edge_out != nullptr && queue.top()->chain_id > 0); + queue.pop(); + } +#endif /* NDEBUG */ + break; + } else { + //FIXME update the 2nd end points on the queue. + // Update end points of the flipped segments. + update_end_point_in_queue(queue, kdtree, chains, end_points, chain.begin->opposite(end_points), first_point_idx, first_point); + update_end_point_in_queue(queue, kdtree, chains, end_points, chain.end->opposite(end_points), first_point_idx, first_point); + if (chain1_flip) + update_end_point_in_queue(queue, kdtree, chains, end_points, *chain.begin, first_point_idx, first_point); + if (chain2_flip) + update_end_point_in_queue(queue, kdtree, chains, end_points, *chain.end, first_point_idx, first_point); + // End points of chains shall certainly stay in the queue. + assert(chain.begin == first_point || chain.begin->heap_idx < queue.size()); + assert(chain.end == first_point || chain.end ->heap_idx < queue.size()); + assert(&chain.begin->opposite(end_points) != first_point && + (chain.begin == first_point ? chain.begin->opposite(end_points).heap_idx_invalid() : chain.begin->opposite(end_points).heap_idx < queue.size())); + assert(&chain.end ->opposite(end_points) != first_point && + (chain.end == first_point ? chain.end ->opposite(end_points).heap_idx_invalid() : chain.end ->opposite(end_points).heap_idx < queue.size())); + + } + } else { + // This edge forms a loop. Update end_point1 and try another one. + update_end_point_in_queue(queue, kdtree, chains, end_points, *end_point1, first_point_idx, first_point); +#ifndef NDEBUG + // Each edge shall be longer than the last one removed from the queue. + //assert(end_point1->distance_out > distance_taken_last - SCALED_EPSILON); + if (end_point1->distance_out < distance_taken_last - SCALED_EPSILON) { +// printf("Warning: taking shorter length than previously is suspicious\n"); + } +#endif /* NDEBUG */ + //FIXME Remove the other end point from the KD tree. + // As the KD tree update is expensive, do it only after some larger number of points is removed from the queue. + } + assert(validate_graph_and_queue()); + } + assert(queue.empty()); + + // Now interconnect pairs of segments into a chain. + assert(first_point != nullptr); + out.reserve(num_segments); + bool failed = false; + do { + assert(out.size() < num_segments); + size_t first_point_id = first_point - &end_points.front(); + size_t segment_id = first_point_id >> 1; + bool reverse = (first_point_id & 1) != 0; + EndPoint *second_point = &end_points[first_point_id ^ 1]; + if (REVERSE_COULD_FAIL) { + if (reverse && ! could_reverse_func(segment_id)) { + failed = true; + break; + } + } else { + assert(! reverse || could_reverse_func(segment_id)); + } + out.emplace_back(segment_id, reverse); + first_point = second_point->edge_out; + } while (first_point != nullptr); + if (REVERSE_COULD_FAIL) { + if (failed) { + if (start_near == nullptr) { + // We may try the reverse order. + out.clear(); + first_point = last_point; + failed = false; + do { + assert(out.size() < num_segments); + size_t first_point_id = first_point - &end_points.front(); + size_t segment_id = first_point_id >> 1; + bool reverse = (first_point_id & 1) != 0; + EndPoint *second_point = &end_points[first_point_id ^ 1]; + if (reverse && ! could_reverse_func(segment_id)) { + failed = true; + break; + } + out.emplace_back(segment_id, reverse); + first_point = second_point->edge_out; + } while (first_point != nullptr); + } + } + if (failed) + // As a last resort, try a dumb algorithm, which is not sensitive to edge reversal constraints. + out = chain_segments_closest_point(end_points, kdtree, could_reverse_func, (initial_point != nullptr) ? *initial_point : end_points.front()); + } else { + assert(! failed); + } + } + + assert(out.size() == num_segments); + return out; +} + template std::vector> chain_segments_greedy_constrained_reversals(SegmentEndPointFunc end_point_func, CouldReverseFunc could_reverse_func, size_t num_segments, const PointType *start_near) { @@ -402,6 +981,19 @@ std::vector> chain_segments_greedy(SegmentEndPointFunc e return chain_segments_greedy_constrained_reversals_(end_point_func, could_reverse_func, num_segments, start_near); } +template +std::vector> chain_segments_greedy_constrained_reversals2(SegmentEndPointFunc end_point_func, CouldReverseFunc could_reverse_func, size_t num_segments, const PointType *start_near) +{ + return chain_segments_greedy_constrained_reversals2_(end_point_func, could_reverse_func, num_segments, start_near); +} + +template +std::vector> chain_segments_greedy2(SegmentEndPointFunc end_point_func, size_t num_segments, const PointType *start_near) +{ + auto could_reverse_func = [](size_t /* idx */) -> bool { return true; }; + return chain_segments_greedy_constrained_reversals2_(end_point_func, could_reverse_func, num_segments, start_near); +} + std::vector> chain_extrusion_entities(std::vector &entities, const Point *start_near) { auto segment_end_point = [&entities](size_t idx, bool first_point) -> const Point& { return first_point ? entities[idx]->first_point() : entities[idx]->last_point(); }; @@ -472,8 +1064,22 @@ std::vector chain_points(const Points &points, Point *start_near) return out; } +#ifndef NDEBUG +// #define DEBUG_SVG_OUTPUT +#endif /* NDEBUG */ + +#ifdef DEBUG_SVG_OUTPUT +void svg_draw_polyline_chain(const char *name, size_t idx, const Polylines &polylines) +{ + BoundingBox bbox = get_extents(polylines); + SVG svg(debug_out_path("%s-%d.svg", name, idx).c_str(), bbox); + svg.draw(polylines); + for (size_t i = 1; i < polylines.size(); ++i) + svg.draw(Line(polylines[i - 1].last_point(), polylines[i].first_point()), "red"); +} +#endif /* DEBUG_SVG_OUTPUT */ + // Flip the sequences of polylines to lower the total length of connecting lines. -// #define DEBUG_SVG_OUTPUT static inline void improve_ordering_by_segment_flipping(Polylines &polylines, bool fixed_start) { #ifndef NDEBUG @@ -487,14 +1093,8 @@ static inline void improve_ordering_by_segment_flipping(Polylines &polylines, bo static int iRun = 0; ++ iRun; - BoundingBox bbox = get_extents(polylines); #ifdef DEBUG_SVG_OUTPUT - { - SVG svg(debug_out_path("improve_ordering_by_segment_flipping-initial-%d.svg", iRun).c_str(), bbox); - svg.draw(polylines); - for (size_t i = 1; i < polylines.size(); ++ i) - svg.draw(Line(polylines[i - 1].last_point(), polylines[i].first_point()), "red"); - } + svg_draw_polyline_chain("improve_ordering_by_segment_flipping-initial", iRun, polylines); #endif /* DEBUG_SVG_OUTPUT */ #endif /* NDEBUG */ @@ -643,34 +1243,285 @@ static inline void improve_ordering_by_segment_flipping(Polylines &polylines, bo #ifndef NDEBUG double cost_final = cost(); #ifdef DEBUG_SVG_OUTPUT + svg_draw_polyline_chain("improve_ordering_by_segment_flipping-final", iRun, polylines); +#endif /* DEBUG_SVG_OUTPUT */ + assert(cost_final <= cost_prev); + assert(cost_final <= cost_initial); +#endif /* NDEBUG */ +} + +struct FlipEdge { + FlipEdge(const Vec2d &p1, const Vec2d &p2, size_t source_index) : p1(p1), p2(p2), source_index(source_index) {} + void flip() { std::swap(this->p1, this->p2); } + Vec2d p1; + Vec2d p2; + size_t source_index; +}; + +struct ConnectionCost { + ConnectionCost(double cost, double cost_flipped) : cost(cost), cost_flipped(cost_flipped) {} + ConnectionCost() : cost(0.), cost_flipped(0.) {} + void flip() { std::swap(this->cost, this->cost_flipped); } + double cost = 0; + double cost_flipped = 0; +}; +static inline ConnectionCost operator-(const ConnectionCost &lhs, const ConnectionCost& rhs) { return ConnectionCost(lhs.cost - rhs.cost, lhs.cost_flipped - rhs.cost_flipped); } + +static inline std::pair minimum_crossover_cost( + const std::vector &edges, + const std::pair &span1, const ConnectionCost &cost1, + const std::pair &span2, const ConnectionCost &cost2, + const std::pair &span3, const ConnectionCost &cost3, + const double cost_current) +{ + auto connection_cost = [&edges]( + const std::pair &span1, const ConnectionCost &cost1, bool reversed1, bool flipped1, + const std::pair &span2, const ConnectionCost &cost2, bool reversed2, bool flipped2, + const std::pair &span3, const ConnectionCost &cost3, bool reversed3, bool flipped3) { + auto first_point = [&edges](const std::pair &span, bool flipped) { return flipped ? edges[span.first].p2 : edges[span.first].p1; }; + auto last_point = [&edges](const std::pair &span, bool flipped) { return flipped ? edges[span.second - 1].p1 : edges[span.second - 1].p2; }; + auto point = [first_point, last_point](const std::pair &span, bool start, bool flipped) { return start ? first_point(span, flipped) : last_point(span, flipped); }; + auto cost = [](const ConnectionCost &acost, bool flipped) { + assert(acost.cost >= 0. && acost.cost_flipped >= 0.); + return flipped ? acost.cost_flipped : acost.cost; + }; + // Ignore reversed single segment spans. + auto simple_span_ignore = [](const std::pair& span, bool reversed) { + return span.first + 1 == span.second && reversed; + }; + assert(span1.first < span1.second); + assert(span2.first < span2.second); + assert(span3.first < span3.second); + return + simple_span_ignore(span1, reversed1) || simple_span_ignore(span2, reversed2) || simple_span_ignore(span3, reversed3) ? + // Don't perform unnecessary calculations simulating reversion of single segment spans. + std::numeric_limits::max() : + // Calculate the cost of reverting chains and / or flipping segment orientations. + cost(cost1, flipped1) + cost(cost2, flipped2) + cost(cost3, flipped3) + + (point(span2, ! reversed2, flipped2) - point(span1, reversed1, flipped1)).norm() + + (point(span3, ! reversed3, flipped3) - point(span2, reversed2, flipped2)).norm(); + }; + +#ifndef NDEBUG { - SVG svg(debug_out_path("improve_ordering_by_segment_flipping-final-%d.svg", iRun).c_str(), bbox); - svg.draw(polylines); - for (size_t i = 1; i < polylines.size(); ++ i) - svg.draw(Line(polylines[i - 1].last_point(), polylines[i].first_point()), "red"); + double c = connection_cost(span1, cost1, false, false, span2, cost2, false, false, span3, cost3, false, false); + assert(std::abs(c - cost_current) < SCALED_EPSILON); } +#endif /* NDEBUG */ + + double cost_min = cost_current; + size_t flip_min = 0; // no flip, no improvement + for (size_t i = 0; i < (1 << 6); ++ i) { + // From the three combinations of 1,2,3 ordering, the other three are reversals of the first three. + double c1 = (i == 0) ? cost_current : + connection_cost(span1, cost1, (i & 1) != 0, (i & (1 << 1)) != 0, span2, cost2, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span3, cost3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0); + double c2 = connection_cost(span1, cost1, (i & 1) != 0, (i & (1 << 1)) != 0, span3, cost3, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span2, cost2, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0); + double c3 = connection_cost(span2, cost2, (i & 1) != 0, (i & (1 << 1)) != 0, span1, cost1, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span3, cost3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0); + if (c1 < cost_min) { + cost_min = c1; + flip_min = i; + } + if (c2 < cost_min) { + cost_min = c2; + flip_min = i + (1 << 6); + } + if (c3 < cost_min) { + cost_min = c3; + flip_min = i + (2 << 6); + } + } + return std::make_pair(cost_min, flip_min); +} + +static inline void do_crossover(const std::vector &edges_in, std::vector &edges_out, + const std::pair &span1, const std::pair &span2, const std::pair &span3, + size_t i) +{ + assert(edges_in.size() == edges_out.size()); + auto do_it = [&edges_in, &edges_out]( + const std::pair &span1, bool reversed1, bool flipped1, + const std::pair &span2, bool reversed2, bool flipped2, + const std::pair &span3, bool reversed3, bool flipped3) { + auto it_edges_out = edges_out.begin(); + auto copy_span = [&edges_in, &edges_out, &it_edges_out](std::pair span, bool reversed, bool flipped) { + assert(span.first < span.second); + auto it = it_edges_out; + if (reversed) + std::reverse_copy(edges_in.begin() + span.first, edges_in.begin() + span.second, it_edges_out); + else + std::copy (edges_in.begin() + span.first, edges_in.begin() + span.second, it_edges_out); + it_edges_out += span.second - span.first; + if (reversed != flipped) { + for (; it != it_edges_out; ++ it) + it->flip(); + } + }; + copy_span(span1, reversed1, flipped1); + copy_span(span2, reversed2, flipped2); + copy_span(span3, reversed3, flipped3); + }; + switch (i >> 6) { + case 0: + do_it(span1, (i & 1) != 0, (i & (1 << 1)) != 0, span2, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0); + break; + case 1: + do_it(span1, (i & 1) != 0, (i & (1 << 1)) != 0, span3, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span2, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0); + break; + default: + assert((i >> 6) == 2); + do_it(span2, (i & 1) != 0, (i & (1 << 1)) != 0, span1, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0); + } + assert(edges_in.size() == edges_out.size()); +} + +static inline void reorder_by_two_exchanges_with_segment_flipping(std::vector &edges) +{ + if (edges.size() < 2) + return; + + std::vector connections(edges.size()); + std::vector edges_tmp(edges); + std::vector> connection_lengths(edges.size() - 1, std::pair(0., 0)); + std::vector connection_tried(edges.size(), false); + for (size_t iter = 0; iter < edges.size(); ++ iter) { + // Initialize connection costs and connection lengths. + for (size_t i = 1; i < edges.size(); ++ i) { + const FlipEdge &e1 = edges[i - 1]; + const FlipEdge &e2 = edges[i]; + ConnectionCost &c = connections[i]; + c = connections[i - 1]; + double l = (e2.p1 - e1.p2).norm(); + c.cost += l; + c.cost_flipped += (e2.p2 - e1.p1).norm(); + connection_lengths[i - 1] = std::make_pair(l, i); + } + std::sort(connection_lengths.begin(), connection_lengths.end(), [](const std::pair &l, const std::pair &r) { return l.first > r.first; }); + std::fill(connection_tried.begin(), connection_tried.end(), false); + size_t crossover1_pos_final = std::numeric_limits::max(); + size_t crossover2_pos_final = std::numeric_limits::max(); + size_t crossover_flip_final = 0; + for (const std::pair &first_crossover_candidate : connection_lengths) { + double longest_connection_length = first_crossover_candidate.first; + size_t longest_connection_idx = first_crossover_candidate.second; + connection_tried[longest_connection_idx] = true; + // Find the second crossover connection with the lowest total chain cost. + size_t crossover_pos_min = std::numeric_limits::max(); + double crossover_cost_min = connections.back().cost; + size_t crossover_flip_min = 0; + for (size_t j = 1; j < connections.size(); ++ j) + if (! connection_tried[j]) { + size_t a = j; + size_t b = longest_connection_idx; + if (a > b) + std::swap(a, b); + std::pair cost_and_flip = minimum_crossover_cost(edges, + std::make_pair(size_t(0), a), connections[a - 1], std::make_pair(a, b), connections[b - 1] - connections[a], std::make_pair(b, edges.size()), connections.back() - connections[b], + connections.back().cost); + if (cost_and_flip.second > 0 && cost_and_flip.first < crossover_cost_min) { + crossover_pos_min = j; + crossover_cost_min = cost_and_flip.first; + crossover_flip_min = cost_and_flip.second; + assert(crossover_cost_min < connections.back().cost + EPSILON); + } + } + if (crossover_cost_min < connections.back().cost) { + // The cost of the chain with the proposed two crossovers has a lower total cost than the current chain. Apply the crossover. + crossover1_pos_final = longest_connection_idx; + crossover2_pos_final = crossover_pos_min; + crossover_flip_final = crossover_flip_min; + break; + } else { + // Continue with another long candidate edge. + } + } + if (crossover_flip_final > 0) { + // Pair of cross over positions and flip / reverse constellation has been found, which improves the total cost of the connection. + // Perform a crossover. + if (crossover1_pos_final > crossover2_pos_final) + std::swap(crossover1_pos_final, crossover2_pos_final); + do_crossover(edges, edges_tmp, std::make_pair(size_t(0), crossover1_pos_final), std::make_pair(crossover1_pos_final, crossover2_pos_final), std::make_pair(crossover2_pos_final, edges.size()), crossover_flip_final); + edges.swap(edges_tmp); + } else { + // No valid pair of cross over positions was found improving the total cost. Giving up. + break; + } + } +} + +// Flip the sequences of polylines to lower the total length of connecting lines. +static inline void improve_ordering_by_two_exchanges_with_segment_flipping(Polylines &polylines, bool fixed_start) +{ +#ifndef NDEBUG + auto cost = [&polylines]() { + double sum = 0.; + for (size_t i = 1; i < polylines.size(); ++i) + sum += (polylines[i].first_point() - polylines[i - 1].last_point()).cast().norm(); + return sum; + }; + double cost_initial = cost(); + + static int iRun = 0; + ++ iRun; +#ifdef DEBUG_SVG_OUTPUT + svg_draw_polyline_chain("improve_ordering_by_two_exchanges_with_segment_flipping-initial", iRun, polylines); #endif /* DEBUG_SVG_OUTPUT */ #endif /* NDEBUG */ - assert(cost_final <= cost_prev); + std::vector edges; + edges.reserve(polylines.size()); + std::transform(polylines.begin(), polylines.end(), std::back_inserter(edges), + [&polylines](const Polyline &pl){ return FlipEdge(pl.first_point().cast(), pl.last_point().cast(), &pl - polylines.data()); }); + reorder_by_two_exchanges_with_segment_flipping(edges); + Polylines out; + out.reserve(polylines.size()); + for (const FlipEdge &edge : edges) { + Polyline &pl = polylines[edge.source_index]; + out.emplace_back(std::move(pl)); + if (edge.p2 == pl.first_point().cast()) { + // Polyline is flipped. + out.back().reverse(); + } else { + // Polyline is not flipped. + assert(edge.p1 == pl.first_point().cast()); + } + } + +#ifndef NDEBUG + double cost_final = cost(); +#ifdef DEBUG_SVG_OUTPUT + svg_draw_polyline_chain("improve_ordering_by_two_exchanges_with_segment_flipping-final", iRun, out); +#endif /* DEBUG_SVG_OUTPUT */ assert(cost_final <= cost_initial); +#endif /* NDEBUG */ } Polylines chain_polylines(Polylines &&polylines, const Point *start_near) { +#ifdef DEBUG_SVG_OUTPUT + static int iRun = 0; + ++ iRun; + svg_draw_polyline_chain("chain_polylines-initial", iRun, polylines); +#endif /* DEBUG_SVG_OUTPUT */ + Polylines out; if (! polylines.empty()) { auto segment_end_point = [&polylines](size_t idx, bool first_point) -> const Point& { return first_point ? polylines[idx].first_point() : polylines[idx].last_point(); }; - std::vector> ordered = chain_segments_greedy(segment_end_point, polylines.size(), start_near); + std::vector> ordered = chain_segments_greedy2(segment_end_point, polylines.size(), start_near); out.reserve(polylines.size()); for (auto &segment_and_reversal : ordered) { out.emplace_back(std::move(polylines[segment_and_reversal.first])); if (segment_and_reversal.second) out.back().reverse(); } - if (out.size() > 1) - improve_ordering_by_segment_flipping(out, start_near != nullptr); + if (out.size() > 1 && start_near == nullptr) { + improve_ordering_by_two_exchanges_with_segment_flipping(out, start_near != nullptr); + //improve_ordering_by_segment_flipping(out, start_near != nullptr); + } } + +#ifdef DEBUG_SVG_OUTPUT + svg_draw_polyline_chain("chain_polylines-final", iRun, out); +#endif /* DEBUG_SVG_OUTPUT */ return out; } diff --git a/tests/libslic3r/test_geometry.cpp b/tests/libslic3r/test_geometry.cpp index 4a800b3d3..6f2bd1c39 100644 --- a/tests/libslic3r/test_geometry.cpp +++ b/tests/libslic3r/test_geometry.cpp @@ -263,6 +263,42 @@ SCENARIO("Path chaining", "[Geometry]") { } } } + GIVEN("Gyroid infill end points") { + Polylines polylines = { + { {28122608, 3221037}, {27919139, 56036027} }, + { {33642863, 3400772}, {30875220, 56450360} }, + { {34579315, 3599827}, {35049758, 55971572} }, + { {26483070, 3374004}, {23971830, 55763598} }, + { {38931405, 4678879}, {38740053, 55077714} }, + { {20311895, 5015778}, {20079051, 54551952} }, + { {16463068, 6773342}, {18823514, 53992958} }, + { {44433771, 7424951}, {42629462, 53346059} }, + { {15697614, 7329492}, {15350896, 52089991} }, + { {48085792, 10147132}, {46435427, 50792118} }, + { {48828819, 10972330}, {49126582, 48368374} }, + { {9654526, 12656711}, {10264020, 47691584} }, + { {5726905, 18648632}, {8070762, 45082416} }, + { {54818187, 39579970}, {52974912, 43271272} }, + { {4464342, 37371742}, {5027890, 39106220} }, + { {54139746, 18417661}, {55177987, 38472580} }, + { {56527590, 32058461}, {56316456, 34067185} }, + { {3303988, 29215290}, {3569863, 32985633} }, + { {56255666, 25025857}, {56478310, 27144087} }, + { {4300034, 22805361}, {3667946, 25752601} }, + { {8266122, 14250611}, {6244813, 17751595} }, + { {12177955, 9886741}, {10703348, 11491900} } + }; + Polylines chained = chain_polylines(polylines); + THEN("Chained taking the shortest path") { + double connection_length = 0.; + for (size_t i = 1; i < chained.size(); ++i) { + const Polyline &pl1 = chained[i - 1]; + const Polyline &pl2 = chained[i]; + connection_length += (pl2.first_point() - pl1.last_point()).cast().norm(); + } + REQUIRE(connection_length < 85206000.); + } + } GIVEN("Loop pieces") { Point a { 2185796, 19058485 }; Point b { 3957902, 18149382 }; From 98255d566ee77e7e371f63f975045da25e5bde9c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 22 Nov 2019 15:35:34 +0100 Subject: [PATCH 67/73] Disabled ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT --- src/slic3r/GUI/Mouse3DController.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 45616091f..161274b5a 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -2,7 +2,7 @@ #define slic3r_Mouse3DController_hpp_ // Enabled debug output to console and extended imgui dialog -#define ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT 1 +#define ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT 0 #include "libslic3r/Point.hpp" From dbbaf411ac5c39c86b1a13bafef9b5fd0f00e6c0 Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 19 Nov 2019 17:22:08 -0300 Subject: [PATCH 68/73] A translation in brazilian portuguese has been added --- resources/localization/pt_br/PrusaSlicer.mo | Bin 0 -> 217077 bytes .../localization/pt_br/PrusaSlicer_pt_br.po | 9386 +++++++++++++++++ 2 files changed, 9386 insertions(+) create mode 100644 resources/localization/pt_br/PrusaSlicer.mo create mode 100644 resources/localization/pt_br/PrusaSlicer_pt_br.po diff --git a/resources/localization/pt_br/PrusaSlicer.mo b/resources/localization/pt_br/PrusaSlicer.mo new file mode 100644 index 0000000000000000000000000000000000000000..a2f3e9789d18d81a6d15de4544a644d9d280221c GIT binary patch literal 217077 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 pDOroPyU{ke*lAWZ?OOX literal 0 HcmV?d00001 diff --git a/resources/localization/pt_br/PrusaSlicer_pt_br.po b/resources/localization/pt_br/PrusaSlicer_pt_br.po new file mode 100644 index 000000000..e54f17ec3 --- /dev/null +++ b/resources/localization/pt_br/PrusaSlicer_pt_br.po @@ -0,0 +1,9386 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-09-09 16:39+0200\n" +"PO-Revision-Date: 2019-11-18 16:39-0300\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Generator: Poedit 2.2.4\n" +"Last-Translator: \n" +"Language: pt_BR\n" + +#: src/slic3r/GUI/AboutDialog.cpp:39 src/slic3r/GUI/AboutDialog.cpp:291 +msgid "Portions copyright" +msgstr "Direitos autorais das partes" + +#: src/slic3r/GUI/AboutDialog.cpp:127 src/slic3r/GUI/AboutDialog.cpp:256 +msgid "Copyright" +msgstr "Direitos autorais" + +#. TRN "Slic3r _is licensed under the_ License" +#: src/slic3r/GUI/AboutDialog.cpp:129 +msgid "" +"License agreements of all following programs (libraries) are part of " +"application license agreement" +msgstr "" +"Os contratos de licença de todos os seguintes programas (bibliotecas) são " +"parte do contrato de licença de aplicativo" + +#: src/slic3r/GUI/AboutDialog.cpp:199 +#, c-format +msgid "About %s" +msgstr "Sobre %s" + +#: src/slic3r/GUI/AboutDialog.cpp:231 src/slic3r/GUI/MainFrame.cpp:62 +msgid "Version" +msgstr "Versão" + +#. TRN "Slic3r _is licensed under the_ License" +#: src/slic3r/GUI/AboutDialog.cpp:258 +msgid "is licensed under the" +msgstr "está licenciado sobre o(a)" + +#: src/slic3r/GUI/AboutDialog.cpp:259 +msgid "GNU Affero General Public License, version 3" +msgstr "Licensa GNU Affero General Public, versão 3" + +#: src/slic3r/GUI/AboutDialog.cpp:260 +msgid "" +"PrusaSlicer is based on Slic3r by Alessandro Ranellucci and the RepRap " +"community." +msgstr "" +"PrusaSlicer é baseado no Slic3r criado por Alessandro Ranellucci e a " +"comunidade RepRap." + +#: src/slic3r/GUI/AboutDialog.cpp:261 +msgid "" +"Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, " +"Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and " +"numerous others." +msgstr "" +"Contribuições por Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, " +"Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik e " +"outros." + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:92 +msgid "" +"Copying of the temporary G-code to the output G-code failed. Maybe the SD " +"card is write locked?" +msgstr "" +"A cópia do G-código provisório G-código falhou na saída. Talvez o cartão SD " +"está bloqueado para escrita?" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:93 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:415 +msgid "Running post-processing scripts" +msgstr "Aplicando scripts de pós-processamento" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:95 +msgid "G-code file exported to %1%" +msgstr "Arquivo G-code exportado para %1%" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:99 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:117 +msgid "Slicing complete" +msgstr "Fatiamento completo" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:113 +msgid "Masked SLA file exported to %1%" +msgstr "Arquivo SLA mascarado exportado para %1%" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:155 +#, c-format +msgid "" +"%s has encountered an error. It was likely caused by running out of memory. " +"If you are sure you have enough RAM on your system, this may also be a bug " +"and we would be glad if you reported it." +msgstr "" +"%s encontrou um erro. Provavelmente foi causado por ficar sem memória. Se " +"você tem certeza que você tem RAM suficiente em seu sistema, isso também " +"pode ser um bug e nós estaríamos contentes se você relatou." + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:417 +msgid "Copying of the temporary G-code to the output G-code failed" +msgstr "A cópia do G-código provisório G-código falhou na saída" + +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:426 +msgid "Scheduling upload to `%1%`. See Window -> Print Host Upload Queue" +msgstr "Agendando upload para ` %1%` . Veja a aba -> Print Host Upload Queue" + +#: src/slic3r/GUI/BedShapeDialog.cpp:65 +msgid "Shape" +msgstr "Forma" + +#: src/slic3r/GUI/BedShapeDialog.cpp:72 +msgid "Rectangular" +msgstr "Retangular" + +#: src/slic3r/GUI/BedShapeDialog.cpp:76 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:393 src/slic3r/GUI/Plater.cpp:145 +#: src/slic3r/GUI/Tab.cpp:2524 +msgid "Size" +msgstr "Tamanho" + +#: src/slic3r/GUI/BedShapeDialog.cpp:77 +msgid "Size in X and Y of the rectangular plate." +msgstr "Tamanho no X e Y na mesa retangular." + +#: src/slic3r/GUI/BedShapeDialog.cpp:83 +msgid "Origin" +msgstr "Origem" + +#: src/slic3r/GUI/BedShapeDialog.cpp:84 +msgid "" +"Distance of the 0,0 G-code coordinate from the front left corner of the " +"rectangle." +msgstr "" +"Distância do ponto 0,0 da coordenada do G-code do canto esquerdo do " +"retângulo." + +#: src/slic3r/GUI/BedShapeDialog.cpp:88 +msgid "Circular" +msgstr "Circular" + +#: src/slic3r/GUI/BedShapeDialog.cpp:91 src/slic3r/GUI/ConfigWizard.cpp:123 +#: src/slic3r/GUI/ConfigWizard.cpp:576 src/slic3r/GUI/ConfigWizard.cpp:590 +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:135 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:390 +#: src/slic3r/GUI/WipeTowerDialog.cpp:84 src/slic3r/GUI/wxExtensions.cpp:509 +#: src/libslic3r/PrintConfig.cpp:70 src/libslic3r/PrintConfig.cpp:77 +#: src/libslic3r/PrintConfig.cpp:86 src/libslic3r/PrintConfig.cpp:220 +#: src/libslic3r/PrintConfig.cpp:295 src/libslic3r/PrintConfig.cpp:303 +#: src/libslic3r/PrintConfig.cpp:353 src/libslic3r/PrintConfig.cpp:363 +#: src/libslic3r/PrintConfig.cpp:488 src/libslic3r/PrintConfig.cpp:499 +#: src/libslic3r/PrintConfig.cpp:517 src/libslic3r/PrintConfig.cpp:695 +#: src/libslic3r/PrintConfig.cpp:1215 src/libslic3r/PrintConfig.cpp:1276 +#: src/libslic3r/PrintConfig.cpp:1294 src/libslic3r/PrintConfig.cpp:1312 +#: src/libslic3r/PrintConfig.cpp:1364 src/libslic3r/PrintConfig.cpp:1374 +#: src/libslic3r/PrintConfig.cpp:1495 src/libslic3r/PrintConfig.cpp:1503 +#: src/libslic3r/PrintConfig.cpp:1544 src/libslic3r/PrintConfig.cpp:1552 +#: src/libslic3r/PrintConfig.cpp:1562 src/libslic3r/PrintConfig.cpp:1570 +#: src/libslic3r/PrintConfig.cpp:1578 src/libslic3r/PrintConfig.cpp:1661 +#: src/libslic3r/PrintConfig.cpp:1878 src/libslic3r/PrintConfig.cpp:1948 +#: src/libslic3r/PrintConfig.cpp:1982 src/libslic3r/PrintConfig.cpp:2176 +#: src/libslic3r/PrintConfig.cpp:2183 src/libslic3r/PrintConfig.cpp:2190 +#: src/libslic3r/PrintConfig.cpp:2220 src/libslic3r/PrintConfig.cpp:2230 +#: src/libslic3r/PrintConfig.cpp:2240 src/libslic3r/PrintConfig.cpp:2403 +#: src/libslic3r/PrintConfig.cpp:2510 src/libslic3r/PrintConfig.cpp:2519 +#: src/libslic3r/PrintConfig.cpp:2528 src/libslic3r/PrintConfig.cpp:2538 +#: src/libslic3r/PrintConfig.cpp:2582 src/libslic3r/PrintConfig.cpp:2592 +#: src/libslic3r/PrintConfig.cpp:2604 src/libslic3r/PrintConfig.cpp:2624 +#: src/libslic3r/PrintConfig.cpp:2634 src/libslic3r/PrintConfig.cpp:2644 +#: src/libslic3r/PrintConfig.cpp:2662 src/libslic3r/PrintConfig.cpp:2677 +#: src/libslic3r/PrintConfig.cpp:2691 src/libslic3r/PrintConfig.cpp:2704 +#: src/libslic3r/PrintConfig.cpp:2742 src/libslic3r/PrintConfig.cpp:2752 +#: src/libslic3r/PrintConfig.cpp:2761 src/libslic3r/PrintConfig.cpp:2771 +msgid "mm" +msgstr "mm" + +#: src/slic3r/GUI/BedShapeDialog.cpp:92 src/libslic3r/PrintConfig.cpp:692 +msgid "Diameter" +msgstr "Diâmetro" + +#: src/slic3r/GUI/BedShapeDialog.cpp:93 +msgid "" +"Diameter of the print bed. It is assumed that origin (0,0) is located in the " +"center." +msgstr "" +"Diâmetro da mesa de impressão. Se assume que a origem (0,0) seja localizado " +"no centro." + +#: src/slic3r/GUI/BedShapeDialog.cpp:97 src/slic3r/GUI/GUI_Preview.cpp:247 +#: src/libslic3r/GCode/PreviewData.cpp:159 +msgid "Custom" +msgstr "Customizado" + +#: src/slic3r/GUI/BedShapeDialog.cpp:101 +msgid "Load shape from STL..." +msgstr "Carregar forma do STL..." + +#: src/slic3r/GUI/BedShapeDialog.cpp:154 +msgid "Settings" +msgstr "config." + +#: src/slic3r/GUI/BedShapeDialog.cpp:171 +msgid "Texture" +msgstr "Textura" + +#: src/slic3r/GUI/BedShapeDialog.cpp:181 src/slic3r/GUI/BedShapeDialog.cpp:249 +msgid "Load..." +msgstr "Carregar..." + +#: src/slic3r/GUI/BedShapeDialog.cpp:189 src/slic3r/GUI/BedShapeDialog.cpp:257 +#: src/slic3r/GUI/Tab.cpp:3286 +msgid "Remove" +msgstr "Remover" + +#: src/slic3r/GUI/BedShapeDialog.cpp:239 +msgid "Model" +msgstr "Modelo" + +#: src/slic3r/GUI/BedShapeDialog.cpp:464 +msgid "Choose an STL file to import bed shape from:" +msgstr "Escolha um arquivo STL para importar o formato da mesa:" + +#: src/slic3r/GUI/BedShapeDialog.cpp:471 src/slic3r/GUI/BedShapeDialog.cpp:520 +#: src/slic3r/GUI/BedShapeDialog.cpp:543 +msgid "Invalid file format." +msgstr "Formato de arquivo inválido." + +#: src/slic3r/GUI/BedShapeDialog.cpp:482 +msgid "Error! Invalid model" +msgstr "Erro! Modelo inválido" + +#: src/slic3r/GUI/BedShapeDialog.cpp:490 +msgid "The selected file contains no geometry." +msgstr "O arquivo selecionado não contém geometria." + +#: src/slic3r/GUI/BedShapeDialog.cpp:494 +msgid "" +"The selected file contains several disjoint areas. This is not supported." +msgstr "O arquivo selecionado contém áreas não juntas. Isso não é suportado." + +#: src/slic3r/GUI/BedShapeDialog.cpp:509 +msgid "Choose a file to import bed texture from (PNG/SVG):" +msgstr "Escolher um arquivo para importar a textura da mesa (PNG/SVG):" + +#: src/slic3r/GUI/BedShapeDialog.cpp:532 +msgid "Choose an STL file to import bed model from:" +msgstr "Escolha um arquivo STL para importar o modelo da mesa:" + +#: src/slic3r/GUI/BedShapeDialog.hpp:59 src/slic3r/GUI/ConfigWizard.cpp:535 +msgid "Bed Shape" +msgstr "Formato da mesa" + +#: src/slic3r/GUI/BonjourDialog.cpp:55 +msgid "Network lookup" +msgstr "Pesquisa de rede" + +#: src/slic3r/GUI/BonjourDialog.cpp:72 +msgid "Address" +msgstr "Endereço" + +#: src/slic3r/GUI/BonjourDialog.cpp:73 +msgid "Hostname" +msgstr "Nome do Host" + +#: src/slic3r/GUI/BonjourDialog.cpp:74 +msgid "Service name" +msgstr "Nome de serviços" + +#: src/slic3r/GUI/BonjourDialog.cpp:76 +msgid "OctoPrint version" +msgstr "Versão do OctoPrint" + +#: src/slic3r/GUI/BonjourDialog.cpp:218 +msgid "Searching for devices" +msgstr "Procurando por dispositivos" + +#: src/slic3r/GUI/BonjourDialog.cpp:225 +msgid "Finished" +msgstr "Finalizado" + +#: src/slic3r/GUI/ButtonsDescription.cpp:16 +msgid "Buttons And Text Colors Description" +msgstr "Descrição dos botões e cores de texto" + +#: src/slic3r/GUI/ButtonsDescription.cpp:36 +msgid "Value is the same as the system value" +msgstr "O valor é o mesmo que o valor do sistema" + +#: src/slic3r/GUI/ButtonsDescription.cpp:53 +msgid "" +"Value was changed and is not equal to the system value or the last saved " +"preset" +msgstr "" +"O valor foi mudado e não é igual ao valor do sistema ou da última config. " +"salva" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:18 +msgid "Upgrade" +msgstr "Atualização" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:20 +msgid "Downgrade" +msgstr "Desatualização" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:22 +msgid "Before roll back" +msgstr "Antes de reverter" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:24 +msgid "User" +msgstr "Usuário" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:27 +msgid "Unknown" +msgstr "Desconhecido" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:39 +msgid "Active" +msgstr "Ativar" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:45 +msgid "slic3r version" +msgstr "versão do slic3r" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:46 src/slic3r/GUI/Preset.cpp:1311 +msgid "print" +msgstr "impressão" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:47 +msgid "filaments" +msgstr "filamentos" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:48 src/slic3r/GUI/Preset.cpp:1315 +msgid "printer" +msgstr "impressora" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:52 src/slic3r/GUI/Tab.cpp:961 +msgid "vendor" +msgstr "fornecedor" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:52 +msgid "version" +msgstr "versão" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:53 +msgid "min slic3r version" +msgstr "versão mínima do slic3r" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:55 +msgid "max slic3r version" +msgstr "versão máxima do slic3r" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:58 +msgid "model" +msgstr "modelo" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:58 +msgid "variants" +msgstr "variantes" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:70 +#, c-format +msgid "Incompatible with this %s" +msgstr "Incompatível com isso %s" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:73 +msgid "Activate" +msgstr "Ativar" + +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:99 +msgid "Configuration Snapshots" +msgstr "config. das versões" + +#: src/slic3r/GUI/ConfigWizard.cpp:123 +msgid "nozzle" +msgstr "bico de impressão" + +#: src/slic3r/GUI/ConfigWizard.cpp:127 +msgid "Alternate nozzles:" +msgstr "Alternar bicos:" + +#: src/slic3r/GUI/ConfigWizard.cpp:193 +msgid "All standard" +msgstr "Todos padrão" + +#: src/slic3r/GUI/ConfigWizard.cpp:194 src/slic3r/GUI/Tab.cpp:3336 +msgid "All" +msgstr "Todos" + +#: src/slic3r/GUI/ConfigWizard.cpp:195 src/slic3r/GUI/Plater.cpp:469 +#: src/slic3r/GUI/Plater.cpp:607 src/libslic3r/GCode/PreviewData.cpp:146 +msgid "None" +msgstr "Nenhum" + +#: src/slic3r/GUI/ConfigWizard.cpp:301 +#, c-format +msgid "Welcome to the %s Configuration Assistant" +msgstr "Bem-vindo ao %s Assistente de config." + +#: src/slic3r/GUI/ConfigWizard.cpp:303 +#, c-format +msgid "Welcome to the %s Configuration Wizard" +msgstr "Bem-vindo ao %s Assistente de config." + +#: src/slic3r/GUI/ConfigWizard.cpp:305 +msgid "Welcome" +msgstr "Bem-vindo(a)" + +#: src/slic3r/GUI/ConfigWizard.cpp:309 src/slic3r/GUI/GUI_App.cpp:793 +#, c-format +msgid "Run %s" +msgstr "Executar %s" + +#: src/slic3r/GUI/ConfigWizard.cpp:311 +#, c-format +msgid "" +"Hello, welcome to %s! This %s helps you with the initial configuration; just " +"a few settings and you will be ready to print." +msgstr "" +"Olá, bem-vindo ao %s! Isso %s te ajuda com a config. inicial; com apenas " +"algumas config. e você estará pronto para imprimir." + +#: src/slic3r/GUI/ConfigWizard.cpp:316 +msgid "" +"Remove user profiles - install from scratch (a snapshot will be taken " +"beforehand)" +msgstr "" +"Remover perfis de usuário - instalar do zero (uma snapshot será salva antes)" + +#: src/slic3r/GUI/ConfigWizard.cpp:347 +#, c-format +msgid "%s Family" +msgstr "%s Família" + +#: src/slic3r/GUI/ConfigWizard.cpp:384 +msgid "Custom Printer Setup" +msgstr "config. da impressora customizada" + +#: src/slic3r/GUI/ConfigWizard.cpp:384 +msgid "Custom Printer" +msgstr "Impressora customizada" + +#: src/slic3r/GUI/ConfigWizard.cpp:386 +msgid "Define a custom printer profile" +msgstr "Definir uma config. para a impressora customizada" + +#: src/slic3r/GUI/ConfigWizard.cpp:388 +msgid "Custom profile name:" +msgstr "Nome customizado da config.:" + +#: src/slic3r/GUI/ConfigWizard.cpp:412 +msgid "Automatic updates" +msgstr "Atualizações automáticas" + +#: src/slic3r/GUI/ConfigWizard.cpp:412 +msgid "Updates" +msgstr "Atualizações" + +#: src/slic3r/GUI/ConfigWizard.cpp:420 src/slic3r/GUI/Preferences.cpp:69 +msgid "Check for application updates" +msgstr "Verificar atualizações nas aplicações" + +#: src/slic3r/GUI/ConfigWizard.cpp:424 +#, c-format +msgid "" +"If enabled, %s checks for new application versions online. When a new " +"version becomes available, a notification is displayed at the next " +"application startup (never during program usage). This is only a " +"notification mechanisms, no automatic installation is done." +msgstr "" +"Se ativada, %s verifica se há novas versões do aplicativo online. Quando uma " +"nova versão se torna disponível, uma notificação é exibida na próxima " +"inicialização do aplicativo (nunca durante o uso do programa). Este é apenas " +"um mecanismos de notificação, nenhuma instalação automática é feita." + +#: src/slic3r/GUI/ConfigWizard.cpp:430 src/slic3r/GUI/Preferences.cpp:77 +msgid "Update built-in Presets automatically" +msgstr "Atualizar predefinições incorporadas automaticamente" + +#: src/slic3r/GUI/ConfigWizard.cpp:434 +#, c-format +msgid "" +"If enabled, %s downloads updates of built-in system presets in the " +"background.These updates are downloaded into a separate temporary location." +"When a new preset version becomes available it is offered at application " +"startup." +msgstr "" +"Se ativada, %s baixa atualizações de predefinições de sistema incorporadas " +"em segundo plano. Essas atualizações são baixadas em um local temporário " +"separado. Quando uma nova versão predefinida se torna disponível, ela é " +"oferecida na inicialização do aplicativo." + +#: src/slic3r/GUI/ConfigWizard.cpp:437 +msgid "" +"Updates are never applied without user's consent and never overwrite user's " +"customized settings." +msgstr "" +"Atualizações nunca são aplicadas sem a permissão do usuário e nunca sobre " +"escrevem as config. do usuário." + +#: src/slic3r/GUI/ConfigWizard.cpp:442 +msgid "" +"Additionally a backup snapshot of the whole configuration is created before " +"an update is applied." +msgstr "" +"Além disso, uma captura de backup de toda a config. é criado antes que uma " +"atualização seja aplicada." + +#: src/slic3r/GUI/ConfigWizard.cpp:449 +msgid "Other Vendors" +msgstr "Outros fornecedores" + +#: src/slic3r/GUI/ConfigWizard.cpp:451 +#, c-format +msgid "Pick another vendor supported by %s:" +msgstr "Escolha outro fornecedor suportado por %s:" + +#: src/slic3r/GUI/ConfigWizard.cpp:497 +msgid "Firmware Type" +msgstr "Tipo de Firmware" + +#: src/slic3r/GUI/ConfigWizard.cpp:497 src/slic3r/GUI/Tab.cpp:2149 +msgid "Firmware" +msgstr "Firmware" + +#: src/slic3r/GUI/ConfigWizard.cpp:501 +msgid "Choose the type of firmware used by your printer." +msgstr "Escolha o tipo de firmware utilizado na sua impressora." + +#: src/slic3r/GUI/ConfigWizard.cpp:535 +msgid "Bed Shape and Size" +msgstr "Forma e tamanho da mesa" + +#: src/slic3r/GUI/ConfigWizard.cpp:538 +msgid "Set the shape of your printer's bed." +msgstr "Insira o formato da mesa de impressão." + +#: src/slic3r/GUI/ConfigWizard.cpp:558 +msgid "Filament and Nozzle Diameters" +msgstr "Diâmetro do bico e do filamento" + +#: src/slic3r/GUI/ConfigWizard.cpp:558 +msgid "Print Diameters" +msgstr "Diâmetros de impressão" + +#: src/slic3r/GUI/ConfigWizard.cpp:572 +msgid "Enter the diameter of your printer's hot end nozzle." +msgstr "Insira o diâmetro do bico de impressão." + +#: src/slic3r/GUI/ConfigWizard.cpp:575 +msgid "Nozzle Diameter:" +msgstr "Diâmetro do bico:" + +#: src/slic3r/GUI/ConfigWizard.cpp:585 +msgid "Enter the diameter of your filament." +msgstr "Coloque o diâmetro do seu filamento." + +#: src/slic3r/GUI/ConfigWizard.cpp:586 +msgid "" +"Good precision is required, so use a caliper and do multiple measurements " +"along the filament, then compute the average." +msgstr "" +"É necessário uma boa precisão, utilize um paquímetro e realize várias " +"medições ao longo do filamento, faça uma média." + +#: src/slic3r/GUI/ConfigWizard.cpp:589 +msgid "Filament Diameter:" +msgstr "Diâmetro do filamento:" + +#: src/slic3r/GUI/ConfigWizard.cpp:623 +msgid "Extruder and Bed Temperatures" +msgstr "Temperaturas da mesa e da extrusora" + +#: src/slic3r/GUI/ConfigWizard.cpp:623 +msgid "Temperatures" +msgstr "Temperaturas" + +#: src/slic3r/GUI/ConfigWizard.cpp:639 +msgid "Enter the temperature needed for extruding your filament." +msgstr "Coloque a temperatura necessária para extrusar seu filamento." + +#: src/slic3r/GUI/ConfigWizard.cpp:640 +msgid "A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS." +msgstr "A regra de ouro é 160 à 230°C para PLA, e 215 à 250°C para ABS." + +#: src/slic3r/GUI/ConfigWizard.cpp:643 +msgid "Extrusion Temperature:" +msgstr "Temperatura de extrusão:" + +#: src/slic3r/GUI/ConfigWizard.cpp:644 src/slic3r/GUI/ConfigWizard.cpp:658 +msgid "°C" +msgstr "°C" + +#: src/slic3r/GUI/ConfigWizard.cpp:653 +msgid "" +"Enter the bed temperature needed for getting your filament to stick to your " +"heated bed." +msgstr "" +"Coloque a temperatura da mesa necessária para fazer com que seu filamento " +"grude na mesa." + +#: src/slic3r/GUI/ConfigWizard.cpp:654 +msgid "" +"A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have " +"no heated bed." +msgstr "" +"A regra de ouro é 60°C para PLA, e 110°C para ABS. Deixe em zero se não há " +"mesa aquecida." + +#: src/slic3r/GUI/ConfigWizard.cpp:657 +msgid "Bed Temperature:" +msgstr "Temperatura da mesa:" + +#: src/slic3r/GUI/ConfigWizard.cpp:1138 +msgid "Select all standard printers" +msgstr "Selecione todas as impressoras padrão" + +#: src/slic3r/GUI/ConfigWizard.cpp:1141 +msgid "< &Back" +msgstr "< &Voltar" + +#: src/slic3r/GUI/ConfigWizard.cpp:1142 +msgid "&Next >" +msgstr "&Próximo >" + +#: src/slic3r/GUI/ConfigWizard.cpp:1143 +msgid "&Finish" +msgstr "&Final" + +#: src/slic3r/GUI/ConfigWizard.cpp:1144 src/slic3r/GUI/FirmwareDialog.cpp:151 +#: src/slic3r/GUI/ProgressStatusBar.cpp:27 +msgid "Cancel" +msgstr "Cancelar" + +#: src/slic3r/GUI/ConfigWizard.cpp:1158 +msgid "Prusa FFF Technology Printers" +msgstr "Impressoras de tecnologia Prusa FFF" + +#: src/slic3r/GUI/ConfigWizard.cpp:1161 +msgid "Prusa MSLA Technology Printers" +msgstr "Impressoras de tecnologia Prusa MSLA" + +#: src/slic3r/GUI/ConfigWizard.cpp:1230 +msgid "Configuration Assistant" +msgstr "Assistente de config." + +#: src/slic3r/GUI/ConfigWizard.cpp:1231 +msgid "Configuration &Assistant" +msgstr "Assistente &de config." + +#: src/slic3r/GUI/ConfigWizard.cpp:1233 +msgid "Configuration Wizard" +msgstr "Assistente de config." + +#: src/slic3r/GUI/ConfigWizard.cpp:1234 +msgid "Configuration &Wizard" +msgstr "Assistente &de config." + +#: src/slic3r/GUI/Field.cpp:125 +msgid "default value" +msgstr "valor padrão" + +#: src/slic3r/GUI/Field.cpp:128 +msgid "parameter name" +msgstr "nome do parâmetro" + +#: src/slic3r/GUI/Field.cpp:139 src/slic3r/GUI/OptionsGroup.cpp:569 +msgid "N/A" +msgstr "N/D" + +#: src/slic3r/GUI/Field.cpp:158 +#, c-format +msgid "%s doesn't support percentage" +msgstr "%s não suporta porcentagem" + +#: src/slic3r/GUI/Field.cpp:174 src/slic3r/GUI/Field.cpp:197 +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:337 +msgid "Invalid numeric input." +msgstr "Entrada numérica não válida." + +#: src/slic3r/GUI/Field.cpp:179 +msgid "Input value is out of range" +msgstr "Valor de entrada está fora do limite" + +#: src/slic3r/GUI/Field.cpp:206 +#, c-format +msgid "" +"Do you mean %s%% instead of %s %s?\n" +"Select YES if you want to change this value to %s%%, \n" +"or NO if you are sure that %s %s is a correct value." +msgstr "" +"Você quer dizer %s%% ao invés de %s %s?\n" +"Selecione SIM se quiser trocar esse valor para %s%%, \n" +"ou NÃO se você tem certeza que %s %s é o valor correto." + +#: src/slic3r/GUI/Field.cpp:209 +msgid "Parameter validation" +msgstr "Validação do parâmetro" + +#: src/slic3r/GUI/FirmwareDialog.cpp:150 +msgid "Flash!" +msgstr "Atualizando!" + +#: src/slic3r/GUI/FirmwareDialog.cpp:152 +msgid "Flashing in progress. Please do not disconnect the printer!" +msgstr "Atualização em progresso. Favor não desconectar sua impressora!" + +#: src/slic3r/GUI/FirmwareDialog.cpp:199 +msgid "Flashing failed" +msgstr "A atualização falhou" + +#: src/slic3r/GUI/FirmwareDialog.cpp:282 +msgid "Flashing succeeded!" +msgstr "Atualizado com sucesso!" + +#: src/slic3r/GUI/FirmwareDialog.cpp:283 +msgid "Flashing failed. Please see the avrdude log below." +msgstr "A atualização falhou. Favor verificar os registros abaixo." + +#: src/slic3r/GUI/FirmwareDialog.cpp:284 +msgid "Flashing cancelled." +msgstr "Atualização cancelada." + +#: src/slic3r/GUI/FirmwareDialog.cpp:332 +#, c-format +msgid "" +"This firmware hex file does not match the printer model.\n" +"The hex file is intended for: %s\n" +"Printer reported: %s\n" +"\n" +"Do you want to continue and flash this hex file anyway?\n" +"Please only continue if you are sure this is the right thing to do." +msgstr "" +"O arquivo hex do firmware não é o mesmo utilizado no modelo da impressora.\n" +"O arquivo hex desejado para: %s\n" +"Impressora relatada: %s\n" +"\n" +"Você gostaria de continuar e atualizar o arquivo hex mesmo assim?\n" +"Favor continuar se tiver certeza que é a coisa certa a se fazer." + +#: src/slic3r/GUI/FirmwareDialog.cpp:419 src/slic3r/GUI/FirmwareDialog.cpp:454 +#, c-format +msgid "" +"Multiple %s devices found. Please only connect one at a time for flashing." +msgstr "" +"Múltiplos %s dispositivos encontrados. Favor conectar um de cada vez para " +"atualização." + +#: src/slic3r/GUI/FirmwareDialog.cpp:436 +#, c-format +msgid "" +"The %s device was not found.\n" +"If the device is connected, please press the Reset button next to the USB " +"connector ..." +msgstr "" +"O %s dispositivo não foi encontrado.\n" +"Se o dispositivo está conectado, favor utilizar o botão de Reset perto do " +"conector USB ..." + +#: src/slic3r/GUI/FirmwareDialog.cpp:548 +#, c-format +msgid "The %s device could not have been found" +msgstr "O %s dispositivo não pode ser encontrado" + +#: src/slic3r/GUI/FirmwareDialog.cpp:645 +#, c-format +msgid "Error accessing port at %s: %s" +msgstr "Erro ao acessa a porta em %s: %s" + +#: src/slic3r/GUI/FirmwareDialog.cpp:647 +#, c-format +msgid "Error: %s" +msgstr "Erro: %s" + +#: src/slic3r/GUI/FirmwareDialog.cpp:777 +msgid "Firmware flasher" +msgstr "Atualizador de Firmware" + +#: src/slic3r/GUI/FirmwareDialog.cpp:802 +msgid "Firmware image:" +msgstr "Imagem do Firmware:" + +#: src/slic3r/GUI/FirmwareDialog.cpp:805 src/slic3r/GUI/Tab.cpp:1870 +#: src/slic3r/GUI/Tab.cpp:1926 +msgid "Browse" +msgstr "Procurar" + +#: src/slic3r/GUI/FirmwareDialog.cpp:807 +msgid "Serial port:" +msgstr "Porte Serial:" + +#: src/slic3r/GUI/FirmwareDialog.cpp:809 +msgid "Autodetected" +msgstr "Auto detectado" + +#: src/slic3r/GUI/FirmwareDialog.cpp:810 +msgid "Rescan" +msgstr "Reescanear" + +#: src/slic3r/GUI/FirmwareDialog.cpp:817 +msgid "Progress:" +msgstr "Progresso:" + +#: src/slic3r/GUI/FirmwareDialog.cpp:820 +msgid "Status:" +msgstr "Status:" + +#: src/slic3r/GUI/FirmwareDialog.cpp:821 +msgid "Ready" +msgstr "Pronto" + +#: src/slic3r/GUI/FirmwareDialog.cpp:841 +msgid "Advanced: Output log" +msgstr "Avançado: log de Saída" + +#: src/slic3r/GUI/FirmwareDialog.cpp:852 +#: src/slic3r/GUI/PrintHostDialogs.cpp:161 +msgid "Close" +msgstr "Fechar" + +#: src/slic3r/GUI/FirmwareDialog.cpp:903 +msgid "" +"Are you sure you want to cancel firmware flashing?\n" +"This could leave your printer in an unusable state!" +msgstr "" +"Você tem certeza que gostaria de cancelar a atualização de Firmware? \n" +"Isso poderia deixar a sua impressora inutilizável!" + +#: src/slic3r/GUI/FirmwareDialog.cpp:904 +msgid "Confirmation" +msgstr "Confirmação" + +#: src/slic3r/GUI/FirmwareDialog.cpp:907 +msgid "Cancelling..." +msgstr "Cancelando..." + +#: src/slic3r/GUI/GLCanvas3D.cpp:534 +msgid "Layers heights" +msgstr "Altura de camada" + +#: src/slic3r/GUI/GLCanvas3D.cpp:631 +msgid "An object outside the print area was detected" +msgstr "Um objeto foi detectado fora da área de impressão" + +#: src/slic3r/GUI/GLCanvas3D.cpp:632 +msgid "A toolpath outside the print area was detected" +msgstr "Há movimentos fora da área de impressão" + +#: src/slic3r/GUI/GLCanvas3D.cpp:633 +msgid "SLA supports outside the print area were detected" +msgstr "Suportes de SLA foram detectados fora da área de impressão" + +#: src/slic3r/GUI/GLCanvas3D.cpp:634 +msgid "Some objects are not visible when editing supports" +msgstr "Alguns objetos não são visíveis quando editando suportes" + +#: src/slic3r/GUI/GLCanvas3D.cpp:636 +msgid "" +"An object outside the print area was detected\n" +"Resolve the current problem to continue slicing" +msgstr "" +"Um objeto foi encontrado fora da área de impressão\n" +"Resolva o problema atual para continuar o fatiamento" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1733 +msgid "Mirror Object" +msgstr "Espelhar objeto" + +#: src/slic3r/GUI/GLCanvas3D.cpp:2970 +msgid "Move Object" +msgstr "Mover objeto" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3506 +msgid "Undo History" +msgstr "Desfazer histórico" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3506 +msgid "Redo History" +msgstr "Refazer histórico" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3524 +#, c-format +msgid "Undo %1$d Action" +msgid_plural "Undo %1$d Actions" +msgstr[0] "Desfazer ação de %1$d" +msgstr[1] "Desfazer ações de %1$d" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3524 +#, c-format +msgid "Redo %1$d Action" +msgid_plural "Redo %1$d Actions" +msgstr[0] "Refazer ação de %1$d" +msgstr[1] "Refazer ações de %1$d" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3571 +msgid "Add..." +msgstr "Adicionar..." + +#: src/slic3r/GUI/GLCanvas3D.cpp:3579 src/slic3r/GUI/GUI_ObjectList.cpp:1501 +#: src/slic3r/GUI/Plater.cpp:3520 src/slic3r/GUI/Plater.cpp:3539 +#: src/slic3r/GUI/Tab.cpp:3286 +msgid "Delete" +msgstr "Deletar" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3588 src/slic3r/GUI/Plater.cpp:4172 +msgid "Delete all" +msgstr "Deletar todos" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3597 src/slic3r/GUI/KBShortcutsDialog.cpp:137 +#: src/slic3r/GUI/Plater.cpp:2681 +msgid "Arrange" +msgstr "Arranjar" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3597 src/slic3r/GUI/KBShortcutsDialog.cpp:138 +msgid "Arrange selection" +msgstr "Arranjar seleção" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3609 +msgid "Copy" +msgstr "Copiar" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3618 +msgid "Paste" +msgstr "Colar" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3630 src/slic3r/GUI/Plater.cpp:3400 +#: src/slic3r/GUI/Plater.cpp:3412 src/slic3r/GUI/Plater.cpp:3526 +msgid "Add instance" +msgstr "Adicionar instância" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3641 src/slic3r/GUI/Plater.cpp:3528 +msgid "Remove instance" +msgstr "Remover instância" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3654 +msgid "Split to objects" +msgstr "Dividir em objetos" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3664 src/slic3r/GUI/GUI_ObjectList.cpp:1340 +msgid "Split to parts" +msgstr "Dividir em partes" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3677 src/slic3r/GUI/GUI_ObjectList.cpp:2203 +msgid "Height ranges" +msgstr "Limites de altura" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3728 src/slic3r/GUI/MainFrame.cpp:570 +msgid "Undo" +msgstr "Desfazer" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3728 src/slic3r/GUI/GLCanvas3D.cpp:3761 +msgid "Click right mouse button to open History" +msgstr "Clique no botão direito para abrir o Histórico" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3745 +msgid "Next Undo action: %1%" +msgstr "Próxima ação de desfazer: %1%" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3761 src/slic3r/GUI/MainFrame.cpp:573 +msgid "Redo" +msgstr "Refazer" + +#: src/slic3r/GUI/GLCanvas3D.cpp:3777 +msgid "Next Redo action: %1%" +msgstr "Próxima ação de refazer: %1%" + +#: src/slic3r/GUI/GLCanvas3D.cpp:5555 +msgid "Selection-Add from rectangle" +msgstr "Seleção-Adicionar do retângulo" + +#: src/slic3r/GUI/GLCanvas3D.cpp:5574 +msgid "Selection-Remove from rectangle" +msgstr "Seleção-remover do retângulo" + +#: src/slic3r/GUI/GLCanvas3DManager.cpp:273 +#, c-format +msgid "" +"PrusaSlicer requires OpenGL 2.0 capable graphics driver to run correctly, \n" +"while OpenGL version %s, render %s, vendor %s was detected." +msgstr "" +"PrusaSlicer requer drivers capazes de executar OpenGL 2.0, \n" +"enquanto a versão do OpenGL %s, renderização %s, fornecedor %s foi detectada." + +#: src/slic3r/GUI/GLCanvas3DManager.cpp:276 +msgid "You may need to update your graphics card driver." +msgstr "Você pode ter que atualizar os drivers da sua placa de vídeo." + +#: src/slic3r/GUI/GLCanvas3DManager.cpp:279 +msgid "" +"As a workaround, you may run PrusaSlicer with a software rendered 3D " +"graphics by running prusa-slicer.exe with the --sw_renderer parameter." +msgstr "" +"Como solução alternativa, você pode executar o PrusaSlicer com um software " +"renderizando gráficos 3D por executar Prusa-slicer.exe com o parâmetro--" +"sw_renderer." + +#: src/slic3r/GUI/GLCanvas3DManager.cpp:281 +msgid "Unsupported OpenGL version" +msgstr "Versão do OpenGL não suportada" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:40 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:145 src/libslic3r/PrintConfig.cpp:3212 +msgid "Cut" +msgstr "Cortar" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:150 +msgid "Keep upper part" +msgstr "Manter parte superior" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:151 +msgid "Keep lower part" +msgstr "Manter parte inferior" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:152 +msgid "Rotate lower part upwards" +msgstr "Rotacione as partes inferiores para cima" + +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:155 +msgid "Perform cut" +msgstr "Aplicar o corte" + +#: src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp:45 +msgid "Place on face" +msgstr "Colocar em uma face" + +#: src/slic3r/GUI/Gizmos/GLGizmoMove.cpp:48 +msgid "Move" +msgstr "Mover" + +#: src/slic3r/GUI/Gizmos/GLGizmoMove.cpp:177 +msgid "Position (mm)" +msgstr "Posição (mm)" + +#: src/slic3r/GUI/Gizmos/GLGizmoMove.cpp:177 +msgid "Displacement (mm)" +msgstr "Deslocamento (mm)" + +#: src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp:449 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:477 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:496 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:514 +#: src/libslic3r/PrintConfig.cpp:3261 +msgid "Rotate" +msgstr "Rotacionar" + +#: src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp:482 +msgid "Rotation (deg)" +msgstr "Rotacionar (graus)" + +#: src/slic3r/GUI/Gizmos/GLGizmoScale.cpp:47 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:392 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:497 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:515 +#: src/libslic3r/PrintConfig.cpp:3276 +msgid "Scale" +msgstr "Escala" + +#: src/slic3r/GUI/Gizmos/GLGizmoScale.cpp:292 +msgid "Scale (%)" +msgstr "Escala (%)" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:44 +msgid "Head diameter" +msgstr "Diâmetro da cabeça" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:45 +msgid "Lock supports under new islands" +msgstr "Travar suportes debaixo de novas ilhas" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:46 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1449 +msgid "Remove selected points" +msgstr "Remover pontos selecionados" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:47 +msgid "Remove all points" +msgstr "Remover todos os pontos" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:48 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1452 +msgid "Apply changes" +msgstr "Aplicar mudanças" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:49 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1453 +msgid "Discard changes" +msgstr "Descartar mudanças" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:50 +msgid "Minimal points distance" +msgstr "Distância mínima entre pontos" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:51 +#: src/libslic3r/PrintConfig.cpp:2651 +msgid "Support points density" +msgstr "Densidade dos pontos de suporte" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:52 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1455 +msgid "Auto-generate points" +msgstr "Pontos gerados automaticamente" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:53 +msgid "Manual editing" +msgstr "Edição manual" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:54 +msgid "Clipping of view" +msgstr "Recorte de vista" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:55 +msgid "Reset direction" +msgstr "Restabelecer direção" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:531 +msgid "Add support point" +msgstr "Adicionar ponto de suporte" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:719 +msgid "Delete support point" +msgstr "Deletar ponto de suporte" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:920 +msgid "Change point head diameter" +msgstr "Mudar o diâmetro do ponto da cabeça" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:986 +msgid "Support parameter change" +msgstr "Mudança de parâmetro de suporte" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1094 +msgid "SLA Support Points" +msgstr "Pontos de suporte SLA" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1115 +msgid "SLA gizmo turned on" +msgstr "Gizmo de SLA ligado" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1137 +msgid "Do you want to save your manually edited support points?" +msgstr "Você deseja salvar os pontos manualmente editados?" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1138 +msgid "Save changes?" +msgstr "Salvar mudanças?" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1150 +msgid "SLA gizmo turned off" +msgstr "Gizmo de SLA desligado" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1187 +msgid "Move support point" +msgstr "Mover pontos de suporte" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1286 +msgid "Support points edit" +msgstr "Edição de pontos de suporte" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1355 +msgid "" +"Autogeneration will erase all manually edited points.\n" +"\n" +"Are you sure you want to do it?\n" +msgstr "" +"Gerar automaticamente irá apagar todos os pontos manualmente editados. Tem " +"certeza que quer gerar?\n" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1357 src/slic3r/GUI/GUI.cpp:289 +#: src/slic3r/GUI/WipeTowerDialog.cpp:44 src/slic3r/GUI/WipeTowerDialog.cpp:328 +msgid "Warning" +msgstr "Aviso" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1360 +msgid "Autogenerate support points" +msgstr "Pontos de suporte gerados automaticamente" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1412 +msgid "SLA gizmo keyboard shortcuts" +msgstr "Atalhos no teclado para gizmo SLA" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1423 +msgid "Note: some shortcuts work in (non)editing mode only." +msgstr "Nota: alguns atalhos funcionam somente em modos que não editam." + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1441 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1444 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1445 +msgid "Left click" +msgstr "Clique esquerdo" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1441 +msgid "Add point" +msgstr "Adicionar ponto" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1442 +msgid "Right click" +msgstr "Clique direito" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1442 +msgid "Remove point" +msgstr "Remover ponto" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1443 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1446 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1447 +msgid "Drag" +msgstr "Arrastar" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1443 +msgid "Move point" +msgstr "Mover ponto" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1444 +msgid "Add point to selection" +msgstr "Adicionar ponto à seleção" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1445 +msgid "Remove point from selection" +msgstr "Remover ponto da seleção" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1446 +msgid "Select by rectangle" +msgstr "Selecionar por retângulo" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1447 +msgid "Deselect by rectangle" +msgstr "Desselecionar por retângulo" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1448 +msgid "Select all points" +msgstr "Selecionar todos os pontos" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1450 +msgid "Mouse wheel" +msgstr "Scroll do mouse" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1450 +msgid "Move clipping plane" +msgstr "Mover plano de recorte" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1451 +msgid "Reset clipping plane" +msgstr "Restabelecer plano de recorte" + +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1454 +msgid "Switch to editing mode" +msgstr "Alterar para modo de edição" + +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:569 +msgid "Gizmo-Place on Face" +msgstr "Gizmo-Colocar em uma face" + +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:641 +msgid "Gizmo-Move" +msgstr "Gizmo-Mover" + +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:646 +msgid "Gizmo-Scale" +msgstr "Gizmo-Escala" + +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:651 +msgid "Gizmo-Rotate" +msgstr "Gizmo-Rotacionar" + +#: src/slic3r/GUI/GUI.cpp:141 src/slic3r/GUI/Tab.cpp:3145 +msgid "It's impossible to print multi-part object(s) with SLA technology." +msgstr "" +"É impossível imprimir objetos com múltiplas partes com a tecnologia SLA." + +#: src/slic3r/GUI/GUI.cpp:142 +msgid "Please check and fix your object list." +msgstr "Favor verificar e concertar sua lista de objetos." + +#: src/slic3r/GUI/GUI.cpp:143 src/slic3r/GUI/Plater.cpp:2246 +#: src/slic3r/GUI/Tab.cpp:3147 +msgid "Attention!" +msgstr "Atenção!" + +#: src/slic3r/GUI/GUI.cpp:283 +msgid "Notice" +msgstr "Aviso" + +#: src/slic3r/GUI/GUI_App.cpp:132 +#, c-format +msgid "" +"%s has encountered an error. It was likely caused by running out of memory. " +"If you are sure you have enough RAM on your system, this may also be a bug " +"and we would be glad if you reported it.\n" +"\n" +"The application will now terminate." +msgstr "" +"%s encontrou um erro. Provavelmente foi causado por ficar sem memória. Se " +"você tem certeza que você tem RAM suficiente em seu sistema, isso também " +"pode ser um bug e nós estaríamos contentes se você relatou.\n" +"\n" +"O aplicativo será encerrado agora." + +#: src/slic3r/GUI/GUI_App.cpp:135 +msgid "Fatal error" +msgstr "Erro fatal" + +#: src/slic3r/GUI/GUI_App.cpp:442 +msgid "Changing of an application language" +msgstr "Alteração de um idioma do aplicativo" + +#: src/slic3r/GUI/GUI_App.cpp:450 src/slic3r/GUI/GUI_App.cpp:459 +msgid "Recreating" +msgstr "Recriando" + +#: src/slic3r/GUI/GUI_App.cpp:463 +msgid "Loading of current presets" +msgstr "Carregando presets" + +#: src/slic3r/GUI/GUI_App.cpp:471 +msgid "Loading of a mode view" +msgstr "Carregamento de um modelo de vista" + +#: src/slic3r/GUI/GUI_App.cpp:551 +msgid "Choose one file (3MF/AMF):" +msgstr "Escolha um arquivo (3MF/AMF):" + +#: src/slic3r/GUI/GUI_App.cpp:563 +msgid "Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):" +msgstr "Escolha um ou mais arquivos (STL/OBJ/AMF/3MF/PRUSA):" + +#: src/slic3r/GUI/GUI_App.cpp:625 +msgid "Select the language" +msgstr "Selecione a linguagem" + +#: src/slic3r/GUI/GUI_App.cpp:625 +msgid "Language" +msgstr "Linguagem" + +#: src/slic3r/GUI/GUI_App.cpp:796 +msgid "&Configuration Snapshots" +msgstr "&Captura das config." + +#: src/slic3r/GUI/GUI_App.cpp:796 +msgid "Inspect / activate configuration snapshots" +msgstr "Inspecionar / ativar capturas de config." + +#: src/slic3r/GUI/GUI_App.cpp:797 +msgid "Take Configuration &Snapshot" +msgstr "Capturar &config." + +#: src/slic3r/GUI/GUI_App.cpp:797 +msgid "Capture a configuration snapshot" +msgstr "Capturar uma config." + +#: src/slic3r/GUI/GUI_App.cpp:800 +msgid "&Preferences" +msgstr "&Preferências" + +#: src/slic3r/GUI/GUI_App.cpp:806 +msgid "Application preferences" +msgstr "Preferências de aplicação" + +#: src/slic3r/GUI/GUI_App.cpp:809 src/slic3r/GUI/wxExtensions.cpp:3043 +msgid "Simple" +msgstr "Simples" + +#: src/slic3r/GUI/GUI_App.cpp:809 +msgid "Simple View Mode" +msgstr "Modo simples de visualização" + +#: src/slic3r/GUI/GUI_App.cpp:810 src/slic3r/GUI/GUI_ObjectList.cpp:97 +#: src/slic3r/GUI/GUI_ObjectList.cpp:620 src/slic3r/GUI/Tab.cpp:1061 +#: src/slic3r/GUI/Tab.cpp:1076 src/slic3r/GUI/Tab.cpp:1174 +#: src/slic3r/GUI/Tab.cpp:1177 src/slic3r/GUI/Tab.cpp:1685 +#: src/slic3r/GUI/Tab.cpp:2169 src/slic3r/GUI/Tab.cpp:3785 +#: src/slic3r/GUI/wxExtensions.cpp:3044 src/libslic3r/PrintConfig.cpp:83 +#: src/libslic3r/PrintConfig.cpp:197 src/libslic3r/PrintConfig.cpp:360 +#: src/libslic3r/PrintConfig.cpp:1013 src/libslic3r/PrintConfig.cpp:2226 +msgid "Advanced" +msgstr "Avançado" + +#: src/slic3r/GUI/GUI_App.cpp:810 +msgid "Advanced View Mode" +msgstr "Modo avançado de visualização" + +#: src/slic3r/GUI/GUI_App.cpp:811 src/slic3r/GUI/wxExtensions.cpp:3045 +msgid "Expert" +msgstr "Especialista" + +#: src/slic3r/GUI/GUI_App.cpp:811 +msgid "Expert View Mode" +msgstr "Modo especialista de visualização" + +#: src/slic3r/GUI/GUI_App.cpp:816 +msgid "Mode" +msgstr "Modo" + +#: src/slic3r/GUI/GUI_App.cpp:816 +#, c-format +msgid "%s View Mode" +msgstr "%s Modo de visualização" + +#: src/slic3r/GUI/GUI_App.cpp:818 +msgid "Change Application &Language" +msgstr "Mudar &idioma" + +#: src/slic3r/GUI/GUI_App.cpp:820 +msgid "Flash printer &firmware" +msgstr "Atualizar firmware &da impressora" + +#: src/slic3r/GUI/GUI_App.cpp:820 +msgid "Upload a firmware image into an Arduino based printer" +msgstr "Atualizar o firmware para uma impressora baseada em Arduino" + +#: src/slic3r/GUI/GUI_App.cpp:832 +msgid "Taking configuration snapshot" +msgstr "Capturando a config." + +#: src/slic3r/GUI/GUI_App.cpp:832 +msgid "Snapshot name" +msgstr "Nome da captura" + +#: src/slic3r/GUI/GUI_App.cpp:875 +msgid "" +"Switching the language will trigger application restart.\n" +"You will lose content of the plater." +msgstr "" +"Alterar a linguagem fará com que o aplicativo reinicie.\n" +"Você irá perder conteúdo no prato." + +#: src/slic3r/GUI/GUI_App.cpp:877 +msgid "Do you want to proceed?" +msgstr "Você quer prosseguir?" + +#: src/slic3r/GUI/GUI_App.cpp:878 +msgid "Language selection" +msgstr "Seleção de linguagem" + +#: src/slic3r/GUI/GUI_App.cpp:901 +msgid "&Configuration" +msgstr "&Configuração" + +#: src/slic3r/GUI/GUI_App.cpp:923 +msgid "The presets on the following tabs were modified" +msgstr "Os presets seguintes foram modificados" + +#: src/slic3r/GUI/GUI_App.cpp:923 src/slic3r/GUI/Tab.cpp:3133 +msgid "Discard changes and continue anyway?" +msgstr "Descartar mudanças e continuar assim mesmo?" + +#: src/slic3r/GUI/GUI_App.cpp:926 +msgid "Unsaved Presets" +msgstr "config. não salvas" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:27 +msgid "Start at height" +msgstr "Começar na altura" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:27 +msgid "Stop at height" +msgstr "Parar na altura" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:27 src/slic3r/GUI/Tab.cpp:1033 +#: src/libslic3r/PrintConfig.cpp:66 +msgid "Layer height" +msgstr "Altura da camada" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:153 +msgid "Remove layer range" +msgstr "Remover limite da camada" + +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:162 +msgid "Add layer range" +msgstr "Adicionar limite da camada" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:34 src/slic3r/GUI/GUI_ObjectList.cpp:88 +#: src/slic3r/GUI/GUI_ObjectList.cpp:611 src/libslic3r/PrintConfig.cpp:67 +#: src/libslic3r/PrintConfig.cpp:160 src/libslic3r/PrintConfig.cpp:392 +#: src/libslic3r/PrintConfig.cpp:453 src/libslic3r/PrintConfig.cpp:461 +#: src/libslic3r/PrintConfig.cpp:867 src/libslic3r/PrintConfig.cpp:1051 +#: src/libslic3r/PrintConfig.cpp:1354 src/libslic3r/PrintConfig.cpp:1420 +#: src/libslic3r/PrintConfig.cpp:1601 src/libslic3r/PrintConfig.cpp:2037 +#: src/libslic3r/PrintConfig.cpp:2095 +msgid "Layers and Perimeters" +msgstr "Camadas e perímetros" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:35 src/slic3r/GUI/GUI_ObjectList.cpp:89 +#: src/slic3r/GUI/GUI_ObjectList.cpp:612 src/slic3r/GUI/Plater.cpp:497 +#: src/slic3r/GUI/Tab.cpp:1065 src/slic3r/GUI/Tab.cpp:1066 +#: src/libslic3r/PrintConfig.cpp:177 src/libslic3r/PrintConfig.cpp:400 +#: src/libslic3r/PrintConfig.cpp:420 src/libslic3r/PrintConfig.cpp:754 +#: src/libslic3r/PrintConfig.cpp:768 src/libslic3r/PrintConfig.cpp:805 +#: src/libslic3r/PrintConfig.cpp:958 src/libslic3r/PrintConfig.cpp:968 +#: src/libslic3r/PrintConfig.cpp:986 src/libslic3r/PrintConfig.cpp:1004 +#: src/libslic3r/PrintConfig.cpp:1023 src/libslic3r/PrintConfig.cpp:1708 +#: src/libslic3r/PrintConfig.cpp:1725 +msgid "Infill" +msgstr "Preenchimento" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:36 src/slic3r/GUI/GUI_ObjectList.cpp:90 +#: src/slic3r/GUI/GUI_ObjectList.cpp:613 src/slic3r/GUI/GUI_Preview.cpp:244 +#: src/slic3r/GUI/Tab.cpp:1094 src/slic3r/GUI/Tab.cpp:1095 +#: src/libslic3r/PrintConfig.cpp:344 src/libslic3r/PrintConfig.cpp:1481 +#: src/libslic3r/PrintConfig.cpp:1830 src/libslic3r/PrintConfig.cpp:1836 +#: src/libslic3r/PrintConfig.cpp:1844 src/libslic3r/PrintConfig.cpp:1856 +#: src/libslic3r/PrintConfig.cpp:1866 src/libslic3r/PrintConfig.cpp:1874 +#: src/libslic3r/PrintConfig.cpp:1889 src/libslic3r/PrintConfig.cpp:1910 +#: src/libslic3r/PrintConfig.cpp:1921 src/libslic3r/PrintConfig.cpp:1937 +#: src/libslic3r/PrintConfig.cpp:1946 src/libslic3r/PrintConfig.cpp:1955 +#: src/libslic3r/PrintConfig.cpp:1966 src/libslic3r/PrintConfig.cpp:1980 +#: src/libslic3r/PrintConfig.cpp:1988 src/libslic3r/PrintConfig.cpp:1989 +#: src/libslic3r/PrintConfig.cpp:1998 src/libslic3r/PrintConfig.cpp:2006 +#: src/libslic3r/PrintConfig.cpp:2020 src/libslic3r/GCode/PreviewData.cpp:156 +msgid "Support material" +msgstr "Material de suporte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:39 src/slic3r/GUI/GUI_ObjectList.cpp:94 +#: src/slic3r/GUI/GUI_ObjectList.cpp:617 src/libslic3r/PrintConfig.cpp:2202 +#: src/libslic3r/PrintConfig.cpp:2210 +msgid "Wipe options" +msgstr "Opções de limpeza" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:45 +msgid "Pad and Support" +msgstr "Bloco e suporte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:51 +msgid "Add part" +msgstr "Adicionar parte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:52 +msgid "Add modifier" +msgstr "Adicionar modificador" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:53 +msgid "Add support enforcer" +msgstr "Adicionar reforço de suporte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:54 +msgid "Add support blocker" +msgstr "Adicionar bloqueador de suporte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:91 src/slic3r/GUI/GUI_ObjectList.cpp:614 +#: src/slic3r/GUI/GUI_Preview.cpp:223 src/slic3r/GUI/Tab.cpp:1119 +#: src/libslic3r/PrintConfig.cpp:209 src/libslic3r/PrintConfig.cpp:441 +#: src/libslic3r/PrintConfig.cpp:896 src/libslic3r/PrintConfig.cpp:1024 +#: src/libslic3r/PrintConfig.cpp:1410 src/libslic3r/PrintConfig.cpp:1647 +#: src/libslic3r/PrintConfig.cpp:1696 src/libslic3r/PrintConfig.cpp:1747 +#: src/libslic3r/PrintConfig.cpp:2080 +msgid "Speed" +msgstr "Velocidade" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:92 src/slic3r/GUI/GUI_ObjectList.cpp:615 +#: src/slic3r/GUI/Tab.cpp:1154 src/slic3r/GUI/Tab.cpp:2043 +#: src/libslic3r/PrintConfig.cpp:471 src/libslic3r/PrintConfig.cpp:979 +#: src/libslic3r/PrintConfig.cpp:1389 src/libslic3r/PrintConfig.cpp:1717 +#: src/libslic3r/PrintConfig.cpp:1902 src/libslic3r/PrintConfig.cpp:1928 +msgid "Extruders" +msgstr "Exrtrusoras" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:93 src/slic3r/GUI/GUI_ObjectList.cpp:616 +#: src/libslic3r/PrintConfig.cpp:431 src/libslic3r/PrintConfig.cpp:538 +#: src/libslic3r/PrintConfig.cpp:855 src/libslic3r/PrintConfig.cpp:987 +#: src/libslic3r/PrintConfig.cpp:1398 src/libslic3r/PrintConfig.cpp:1737 +#: src/libslic3r/PrintConfig.cpp:1911 src/libslic3r/PrintConfig.cpp:2069 +msgid "Extrusion Width" +msgstr "Espessura da extrusão" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:99 src/slic3r/GUI/GUI_ObjectList.cpp:622 +#: src/slic3r/GUI/Plater.cpp:465 src/slic3r/GUI/Tab.cpp:3737 +#: src/slic3r/GUI/Tab.cpp:3738 src/libslic3r/PrintConfig.cpp:2501 +#: src/libslic3r/PrintConfig.cpp:2508 src/libslic3r/PrintConfig.cpp:2517 +#: src/libslic3r/PrintConfig.cpp:2526 src/libslic3r/PrintConfig.cpp:2536 +#: src/libslic3r/PrintConfig.cpp:2562 src/libslic3r/PrintConfig.cpp:2569 +#: src/libslic3r/PrintConfig.cpp:2580 src/libslic3r/PrintConfig.cpp:2590 +#: src/libslic3r/PrintConfig.cpp:2599 src/libslic3r/PrintConfig.cpp:2612 +#: src/libslic3r/PrintConfig.cpp:2622 src/libslic3r/PrintConfig.cpp:2631 +#: src/libslic3r/PrintConfig.cpp:2641 src/libslic3r/PrintConfig.cpp:2652 +#: src/libslic3r/PrintConfig.cpp:2660 +msgid "Supports" +msgstr "Suportes" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:100 src/slic3r/GUI/GUI_ObjectList.cpp:623 +#: src/slic3r/GUI/Plater.cpp:603 src/slic3r/GUI/Tab.cpp:3769 +#: src/slic3r/GUI/Tab.cpp:3770 src/libslic3r/PrintConfig.cpp:2668 +#: src/libslic3r/PrintConfig.cpp:2675 src/libslic3r/PrintConfig.cpp:2689 +#: src/libslic3r/PrintConfig.cpp:2699 src/libslic3r/PrintConfig.cpp:2721 +#: src/libslic3r/PrintConfig.cpp:2732 src/libslic3r/PrintConfig.cpp:2739 +#: src/libslic3r/PrintConfig.cpp:2750 src/libslic3r/PrintConfig.cpp:2759 +#: src/libslic3r/PrintConfig.cpp:2768 +msgid "Pad" +msgstr "Bloco" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:260 +msgid "Name" +msgstr "Nome" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:271 src/slic3r/GUI/GUI_ObjectList.cpp:373 +msgid "Editing" +msgstr "Edição" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:318 +#, c-format +msgid "Auto-repaired (%d errors):\n" +msgstr "Auto reparando (%d erros):\n" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:325 +msgid "degenerate facets" +msgstr "facetas degeneradas" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:326 +msgid "edges fixed" +msgstr "arestas fixadas" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:327 +msgid "facets removed" +msgstr "facetas removidas" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:328 +msgid "facets added" +msgstr "facetas adicionadas" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:329 +msgid "facets reversed" +msgstr "facetas reversidas" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:330 +msgid "backwards edges" +msgstr "arestas viradas para trás" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:338 +msgid "Right button click the icon to fix STL through Netfabb" +msgstr "" +"Clique com o botão direito no ícone para arrumar STL através do Netfabb" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:375 +msgid "Right button click the icon to change the object settings" +msgstr "Clique com o botão direito no ícone para mudar as config. do objeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:377 +msgid "Click the icon to change the object settings" +msgstr "Clique no ícone para mudar as config. do objeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:381 +msgid "Right button click the icon to change the object printable property" +msgstr "" +"Clique com o botão direito no ícone para mudar a propriedade de impressão do " +"objeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:383 +msgid "Click the icon to change the object printable property" +msgstr "Clique no ícone para mudar a propriedade de impressão do objeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:428 src/slic3r/GUI/GUI_ObjectList.cpp:449 +#: src/slic3r/GUI/GUI_ObjectList.cpp:461 src/slic3r/GUI/GUI_ObjectList.cpp:3642 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3652 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3684 src/slic3r/GUI/wxExtensions.cpp:603 +#: src/slic3r/GUI/wxExtensions.cpp:660 src/slic3r/GUI/wxExtensions.cpp:685 +#: src/slic3r/GUI/wxExtensions.cpp:893 +msgid "default" +msgstr "padrão" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:433 src/slic3r/GUI/Tab.cpp:1649 +#: src/libslic3r/PrintConfig.cpp:470 +msgid "Extruder" +msgstr "Extrusora" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:546 +msgid "Rename Object" +msgstr "Renomear objeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:546 +msgid "Rename Sub-object" +msgstr "Renomear sub-objeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:987 src/slic3r/GUI/GUI_ObjectList.cpp:3464 +msgid "Instances to Separated Objects" +msgstr "Instâncias para separar objetos" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1005 +msgid "Volumes in Object reordered" +msgstr "Volume reorganizados no objeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1005 +msgid "Object reordered" +msgstr "Objeto reorganizado" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1060 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1376 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1382 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1623 +#, c-format +msgid "Quick Add Settings (%s)" +msgstr "Adicionar config. rapidamente (%s)" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1137 +msgid "Select showing settings" +msgstr "Selecionar config. mostradas" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1186 +msgid "Add Settings for Layers" +msgstr "Adicionar config. para camadas" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1187 +msgid "Add Settings for Sub-object" +msgstr "Adicionar config. para sub-objetos" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1188 +msgid "Add Settings for Object" +msgstr "Adicionar config. para objetos" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1249 +msgid "Add Settings Bundle for Height range" +msgstr "Adicionar pacote de config. para intervalo de altura" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1250 +msgid "Add Settings Bundle for Sub-object" +msgstr "Adicionar pacote de config. para subobjeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1251 +msgid "Add Settings Bundle for Object" +msgstr "Adicionar pacote de config. para objeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1290 +msgid "Load" +msgstr "Carregar" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1295 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1320 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1323 +msgid "Box" +msgstr "Caixa" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1295 +msgid "Cylinder" +msgstr "Cilindro" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1295 +msgid "Sphere" +msgstr "Esfera" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1295 +msgid "Slab" +msgstr "Placa" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1347 +msgid "Height range Modifier" +msgstr "Modificador de intervalo de altura" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1355 +msgid "Add settings" +msgstr "Adicionar config." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1422 +msgid "Change type" +msgstr "Mudar o tipo" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1429 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1577 +msgid "Set as a Separated Object" +msgstr "Configurar como objeto separado" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1435 +msgid "Printable" +msgstr "Imprimível" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1442 +msgid "Rename" +msgstr "Renomear" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1453 +msgid "Fix through the Netfabb" +msgstr "Arrumar através do Netfabb" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1463 src/slic3r/GUI/Plater.cpp:3552 +msgid "Export as STL" +msgstr "Exportar como STL" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1470 +msgid "Change extruder" +msgstr "Mudar extrusora" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1489 src/libslic3r/PrintConfig.cpp:309 +msgid "Default" +msgstr "Padrão" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1495 +msgid "Select new extruder for the object/part" +msgstr "Selecionar nova extrusora para objeto/parte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1507 +msgid "Scale to print volume" +msgstr "Escalar para volume de impressão" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1507 +msgid "Scale the selected object to fit the print volume" +msgstr "Escale o objeto selecionado para se adequar ao volume de impressão" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1577 +msgid "Set as a Separated Objects" +msgstr "Definir como objetos separados" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1652 +msgid "Load Part" +msgstr "Carregar parte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1687 +msgid "Error!" +msgstr "Erro!" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1732 +msgid "Add Generic Subobject" +msgstr "Adicionar sub-objeto genérico" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1739 +msgid "Generic" +msgstr "Genérico" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1843 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1945 +msgid "Last instance of an object cannot be deleted." +msgstr "A última instância de um objeto não pode ser excluída." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1855 +msgid "Delete Settings" +msgstr "Deletar config." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1879 +msgid "Delete All Instances from Object" +msgstr "Excluir todas as instâncias do objeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1895 +msgid "Delete Height Range" +msgstr "Excluir limite de altura" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1926 +msgid "From Object List You can't delete the last solid part from object." +msgstr "" +"Na lista de objetos não é possível excluir a última parte sólida do objeto." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1930 +msgid "Delete Subobject" +msgstr "Deletar sub-objeto" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1949 +msgid "Delete Instance" +msgstr "Deletar instância" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1973 src/slic3r/GUI/Plater.cpp:2838 +msgid "" +"The selected object couldn't be split because it contains only one part." +msgstr "O seguinte objeto não pode ser dividido pois contém uma parte." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:1977 +msgid "Split to Parts" +msgstr "Dividir em partes" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2025 +msgid "Add Layers" +msgstr "Adicionar camadas" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2150 +msgid "Group manipulation" +msgstr "Manipulação de grupos" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2162 +msgid "Object manipulation" +msgstr "Manipulação de objetos" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2175 +msgid "Object Settings to modify" +msgstr "config. do objeto para modificar" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2179 +msgid "Part Settings to modify" +msgstr "config. da parte para modificar" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2184 +msgid "Layer range Settings to modify" +msgstr "config. de intervalo de camada para modificar" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2190 +msgid "Part manipulation" +msgstr "Manipulação da parte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2196 +msgid "Instance manipulation" +msgstr "Manipulação da instância" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2203 +msgid "Settings for height range" +msgstr "config. para intervalo de altura" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2388 +msgid "Delete Selected Item" +msgstr "Excluir item selecionado" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2525 +msgid "Delete Selected" +msgstr "Excluir seleção" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2584 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2613 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2631 +msgid "Add Height Range" +msgstr "Adicionar intervalo de altura" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2690 +msgid "Edit Height Range" +msgstr "Editar intervalo de altura" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2974 +msgid "Selection-Remove from list" +msgstr "Seleção-Remover da lista" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:2982 +msgid "Selection-Add from list" +msgstr "Seleção-Adicionar da lista" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3100 +msgid "Object or Instance" +msgstr "Objeto ou instância" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3101 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3234 +msgid "Part" +msgstr "Parte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3101 +msgid "Layer" +msgstr "Camada" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3103 +msgid "Unsupported selection" +msgstr "Seleção não suportada" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3104 +#, c-format +msgid "You started your selection with %s Item." +msgstr "Você iniciou sua seleção com o item de %s." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3105 +#, c-format +msgid "In this mode you can select only other %s Items%s" +msgstr "Neste modo, você pode selecionar apenas outros %s itens%s" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3108 +msgid "of a current Object" +msgstr "de um objeto atual" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3113 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3188 src/slic3r/GUI/Plater.cpp:126 +msgid "Info" +msgstr "Informação" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3229 +msgid "You can't change a type of the last solid part of the object." +msgstr "Não é possível alterar um tipo da última parte sólida do objeto." + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3234 +msgid "Modifier" +msgstr "Modificador" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3234 +msgid "Support Enforcer" +msgstr "Reforçador de suporte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3234 +msgid "Support Blocker" +msgstr "Bloqueador de suporte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3236 +msgid "Type:" +msgstr "Tipo:" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3236 +msgid "Select type of part" +msgstr "Selecione o tipo de parte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3241 +msgid "Change Part Type" +msgstr "Mudar o tipo da parte" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3486 +msgid "Enter new name" +msgstr "Insira o novo nome" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3486 +msgid "Renaming" +msgstr "Renomeando" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3502 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3608 src/slic3r/GUI/Tab.cpp:3618 +#: src/slic3r/GUI/Tab.cpp:3622 +msgid "The supplied name is not valid;" +msgstr "O nome inserido não é valido;" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3503 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3609 src/slic3r/GUI/Tab.cpp:3619 +msgid "the following characters are not allowed:" +msgstr "os seguintes caracteres não são permitidos:" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3632 +msgid "Set extruder for selected items" +msgstr "Definir extrusora para itens selecionados" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3633 +msgid "Select extruder number for selected objects and/or parts" +msgstr "Selecione o número da extrusora para objetos e/ou peças selecionados" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3646 +msgid "Select extruder number:" +msgstr "Selecione o número da extrusora:" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3647 +msgid "This extruder will be set for selected items" +msgstr "Esta extrusora será ajustada para artigos selecionados" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3759 src/slic3r/GUI/Selection.cpp:1473 +msgid "Set Printable" +msgstr "Definir como imprimível" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:3759 src/slic3r/GUI/Selection.cpp:1473 +msgid "Set Unprintable" +msgstr "Definir não imprimível" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:62 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:105 +msgid "World coordinates" +msgstr "Coordenadas mundiais" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:63 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:106 +msgid "Local coordinates" +msgstr "Coordenadas locais" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:82 +msgid "Select coordinate space, in which the transformation will be performed." +msgstr "" +"Selecione o espaço de coordenadas, no qual a transformação será executada." + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:125 +msgid "Object Manipulation" +msgstr "Manipulação de objeto" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:178 +msgid "Object name" +msgstr "Nome do objeto" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:214 +#, c-format +msgid "Toggle %c axis mirroring" +msgstr "Ativar espelhamento do eixo %c" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:247 +msgid "Set Mirror" +msgstr "Definir espelhamento" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:287 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:292 +msgid "Reset scale" +msgstr "Restabelecer escala" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:305 +msgid "Reset rotation" +msgstr "Restabelecer rotação" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:330 +msgid "Reset Rotation" +msgstr "Restabelecer Rotação" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:342 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:357 +msgid "Drop to bed" +msgstr "Soltar na mesa" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:390 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:454 +msgid "Position" +msgstr "Posição" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:391 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:455 +msgid "Rotation" +msgstr "Rotação" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:456 +msgid "Scale factors" +msgstr "Fatores de escala" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:513 +msgid "Translate" +msgstr "Tradução" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:565 +msgid "" +"You cannot use non-uniform scaling mode for multiple objects/parts selection" +msgstr "" +"Não é possível usar o modo de dimensionamento não uniforme para vários " +"objetos/seleção de peças" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:735 +msgid "Set Position" +msgstr "Definir posição" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:766 +msgid "Set Orientation" +msgstr "Definir orientação" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:831 +msgid "Set Scale" +msgstr "Definir escala" + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:915 +msgid "" +"The currently manipulated object is tilted (rotation angles are not " +"multiples of 90°).\n" +"Non-uniform scaling of tilted objects is only possible in the World " +"coordinate system,\n" +"once the rotation is embedded into the object coordinates." +msgstr "" +"O objeto atualmente manipulado é inclinado (os ângulos de rotação não são " +"múltiplos de 90 °).\n" +"O dimensionamento não uniforme de objetos inclinados só é possível no " +"sistema de coordenadas do mundo,\n" +"uma vez que a rotação é incorporada nas coordenadas do objeto." + +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:918 +msgid "" +"This operation is irreversible.\n" +"Do you want to proceed?" +msgstr "" +"Esta operação é irreversível.\n" +"Você quer prosseguir?" + +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:59 +msgid "Additional Settings" +msgstr "config. Adicionais" + +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:95 +msgid "Remove parameter" +msgstr "Remover parâmetro" + +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:101 +#, c-format +msgid "Delete Option %s" +msgstr "Excluir opção %s" + +#: src/slic3r/GUI/GUI_ObjectSettings.cpp:146 +#, c-format +msgid "Change Option %s" +msgstr "Alterar opção %s" + +#: src/slic3r/GUI/GUI_Preview.cpp:217 +msgid "View" +msgstr "Vista" + +#: src/slic3r/GUI/GUI_Preview.cpp:220 src/slic3r/GUI/GUI_Preview.cpp:569 +#: src/libslic3r/GCode/PreviewData.cpp:378 +msgid "Feature type" +msgstr "Tipo de recurso" + +#: src/slic3r/GUI/GUI_Preview.cpp:221 src/libslic3r/PrintConfig.cpp:483 +msgid "Height" +msgstr "Altura" + +#: src/slic3r/GUI/GUI_Preview.cpp:222 src/libslic3r/PrintConfig.cpp:2188 +msgid "Width" +msgstr "Espessura" + +#: src/slic3r/GUI/GUI_Preview.cpp:224 +msgid "Volumetric flow rate" +msgstr "Taxa de fluxo volumétrico" + +#: src/slic3r/GUI/GUI_Preview.cpp:225 src/slic3r/GUI/GUI_Preview.cpp:333 +#: src/slic3r/GUI/GUI_Preview.cpp:515 src/slic3r/GUI/GUI_Preview.cpp:568 +#: src/slic3r/GUI/GUI_Preview.cpp:774 src/libslic3r/GCode/PreviewData.cpp:388 +msgid "Tool" +msgstr "Ferramenta" + +#: src/slic3r/GUI/GUI_Preview.cpp:226 src/slic3r/GUI/GUI_Preview.cpp:566 +#: src/libslic3r/GCode/PreviewData.cpp:390 +msgid "Color Print" +msgstr "Impressão colorida" + +#: src/slic3r/GUI/GUI_Preview.cpp:229 +msgid "Show" +msgstr "Mostrar" + +#: src/slic3r/GUI/GUI_Preview.cpp:232 src/slic3r/GUI/GUI_Preview.cpp:233 +msgid "Feature types" +msgstr "Tipos de características" + +#: src/slic3r/GUI/GUI_Preview.cpp:235 src/libslic3r/GCode/PreviewData.cpp:147 +msgid "Perimeter" +msgstr "Perímetro" + +#: src/slic3r/GUI/GUI_Preview.cpp:236 src/libslic3r/GCode/PreviewData.cpp:148 +msgid "External perimeter" +msgstr "Perímetro externo" + +#: src/slic3r/GUI/GUI_Preview.cpp:237 src/libslic3r/GCode/PreviewData.cpp:149 +msgid "Overhang perimeter" +msgstr "Perímetro de angulação" + +#: src/slic3r/GUI/GUI_Preview.cpp:238 src/libslic3r/GCode/PreviewData.cpp:150 +msgid "Internal infill" +msgstr "Preenchimento interno" + +#: src/slic3r/GUI/GUI_Preview.cpp:239 src/libslic3r/PrintConfig.cpp:1736 +#: src/libslic3r/PrintConfig.cpp:1746 src/libslic3r/GCode/PreviewData.cpp:151 +msgid "Solid infill" +msgstr "Preenchimento sólido" + +#: src/slic3r/GUI/GUI_Preview.cpp:240 src/libslic3r/PrintConfig.cpp:2068 +#: src/libslic3r/PrintConfig.cpp:2079 src/libslic3r/GCode/PreviewData.cpp:152 +msgid "Top solid infill" +msgstr "Preenchimento do sólido do topo" + +#: src/slic3r/GUI/GUI_Preview.cpp:241 src/libslic3r/GCode/PreviewData.cpp:153 +msgid "Bridge infill" +msgstr "Preenchimento de pontes" + +#: src/slic3r/GUI/GUI_Preview.cpp:242 src/libslic3r/PrintConfig.cpp:895 +#: src/libslic3r/GCode/PreviewData.cpp:154 +msgid "Gap fill" +msgstr "Preenchimento de vão" + +#: src/slic3r/GUI/GUI_Preview.cpp:243 src/slic3r/GUI/Tab.cpp:1085 +#: src/libslic3r/GCode/PreviewData.cpp:155 +msgid "Skirt" +msgstr "Saia" + +#: src/slic3r/GUI/GUI_Preview.cpp:245 src/libslic3r/PrintConfig.cpp:1954 +#: src/libslic3r/GCode/PreviewData.cpp:157 +msgid "Support material interface" +msgstr "Interface do material de suporte" + +#: src/slic3r/GUI/GUI_Preview.cpp:246 src/slic3r/GUI/Tab.cpp:1165 +#: src/libslic3r/GCode/PreviewData.cpp:158 +msgid "Wipe tower" +msgstr "Torre de limpeza" + +#: src/slic3r/GUI/GUI_Preview.cpp:251 src/libslic3r/PrintConfig.cpp:2102 +msgid "Travel" +msgstr "Viagem" + +#: src/slic3r/GUI/GUI_Preview.cpp:252 +msgid "Retractions" +msgstr "Retrações" + +#: src/slic3r/GUI/GUI_Preview.cpp:253 +msgid "Unretractions" +msgstr "Retorno da retração" + +#: src/slic3r/GUI/GUI_Preview.cpp:254 +msgid "Shells" +msgstr "Paredes" + +#: src/slic3r/GUI/GUI_Preview.cpp:255 +msgid "Legend" +msgstr "Legenda" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:14 src/slic3r/GUI/MainFrame.cpp:683 +msgid "Keyboard Shortcuts" +msgstr "Atalhos do teclado" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:107 +msgid "Open project STL/OBJ/AMF/3MF with config, delete bed" +msgstr "Abra o projeto STL/OBJ/AMF/3MF com config., excluir mesa" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:108 +msgid "Import STL/OBJ/AMF/3MF without config, keep bed" +msgstr "Importação STL/OBJ/AMF/3MF sem config., manter a mesa" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:109 +msgid "Load Config from .ini/amf/3mf/gcode" +msgstr "Carregar config. de um .ini/AMF/3mf/Gcode" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:110 src/slic3r/GUI/Plater.cpp:837 +#: src/slic3r/GUI/Plater.cpp:4822 src/libslic3r/PrintConfig.cpp:3163 +msgid "Export G-code" +msgstr "Exportar G-code" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:111 +msgid "Save project (3MF)" +msgstr "Salvar projeto (3MF)" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:112 +msgid "Load Config from .ini/amf/3mf/gcode and merge" +msgstr "Carregar config. de um. ini/AMF/3mf/Gcode e mesclar" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:113 +msgid "(Re)slice" +msgstr "(Re)fatiar" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:116 +msgid "Select Plater Tab" +msgstr "Selecione a guia de prato" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:118 +msgid "Select Print Settings Tab" +msgstr "Selecione a guia config. de impressão" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:119 +msgid "Select Filament Settings Tab" +msgstr "Selecione a guia config. de filamento" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:120 +msgid "Select Printer Settings Tab" +msgstr "Selecione a guia config. da impressora" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:121 +msgid "Switch to 3D" +msgstr "Mude para 3D" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:122 +msgid "Switch to Preview" +msgstr "Mudar para pré-visualização" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:123 src/slic3r/GUI/Preferences.cpp:10 +msgid "Preferences" +msgstr "Preferências" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:124 +#: src/slic3r/GUI/PrintHostDialogs.cpp:136 +msgid "Print host upload queue" +msgstr "Fila de carregamento do host de impressão" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:125 +msgid "Camera view" +msgstr "Vista da câmera" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:126 +msgid "Add Instance of the selected object" +msgstr "Adicionar instância do objeto selecionado" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:127 +msgid "Remove Instance of the selected object" +msgstr "Remover instância do objeto selecionado" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:128 +msgid "Show keyboard shortcuts list" +msgstr "Mostrar lista dos atalhos no teclado" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:129 +msgid "Press to select multiple object or move multiple object with mouse" +msgstr "" +"Aperte para selecionar múltiplos objetos ou mover múltiplos objetos com o " +"mouse" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:131 +msgid "Main Shortcuts" +msgstr "Atalhos principais" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:139 +msgid "Select All objects" +msgstr "Selecionar todos os objetos" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:140 +msgid "Delete selected" +msgstr "Deletar seleção" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:141 +msgid "Delete All" +msgstr "Deletar todos" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:142 +msgid "Copy to clipboard" +msgstr "Copiar para a área de transferência" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:143 +msgid "Paste from clipboard" +msgstr "Colar da área de transferência" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:144 +msgid "Gizmo move" +msgstr "Gizmo-Mover" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:145 +msgid "Gizmo scale" +msgstr "Gizmo-Escala" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:146 +msgid "Gizmo rotate" +msgstr "Gizmo-Rotacionar" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:147 +msgid "Gizmo cut" +msgstr "Gizmo-Cortar" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:148 +msgid "Gizmo Place face on bed" +msgstr "Colocar face do Gizmo na mesa" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:149 +msgid "Gizmo SLA support points" +msgstr "Pontos de suporte do Gizmo SLA" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:150 +#, c-format +msgid "" +"Press to activate selection rectangle\n" +"or to snap by 5% in Gizmo scale\n" +"or to snap by 1mm in Gizmo move" +msgstr "" +"Pressione para ativar o retângulo de seleção\n" +"ou para encaixar em 5% i na escala Gizmo\n" +"ou para encaixar por 1mm em Gizmo mover" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:151 +msgid "" +"Press to scale selection to fit print volume\n" +"in Gizmo scale" +msgstr "" +"Pressione para dimensionar a seleção ao volume de impressão\n" +"na escala Gizmo" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:152 +msgid "" +"Press to activate deselection rectangle\n" +"or to scale or rotate selected objects\n" +"around their own center" +msgstr "" +"Pressione para ativar o retângulo de deseleção\n" +"ou para dimensionar ou girar objetos selecionados\n" +"em torno de seu próprio centro" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:153 +msgid "Press to activate one direction scaling in Gizmo scale" +msgstr "Pressione para ativar um dimensionamento de direção na escala Gizmo" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:154 +msgid "Change camera type (perspective, orthographic)" +msgstr "Alterar tipo de câmera (perspectiva, ortográfica)" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:155 +msgid "Zoom to Bed" +msgstr "Ampliar para a mesa" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:156 +msgid "Zoom to all objects in scene, if none selected" +msgstr "Ampliar para todos os objetos na cena, se nenhum selecionado" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:157 +msgid "Zoom to selected object" +msgstr "Ampliar para o objeto selecionado" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:158 +msgid "Zoom in" +msgstr "Ampliar" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:159 +msgid "Zoom out" +msgstr "Dimiuir" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:160 +msgid "Unselect gizmo / Clear selection" +msgstr "Desmarcar Gizmo/limpar seleção" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:166 +msgid "Plater Shortcuts" +msgstr "Atalhos do prato" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:181 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:193 +msgid "Arrow Up" +msgstr "Seta para cima" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:181 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:183 +msgid "Upper Layer" +msgstr "Camada superior" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:182 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:194 +msgid "Arrow Down" +msgstr "Seta para baixo" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:182 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:184 +msgid "Lower Layer" +msgstr "Camada inferior" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:185 +msgid "Show/Hide (L)egend" +msgstr "Mostrar/ocultar (L) egenda" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:187 +msgid "Preview Shortcuts" +msgstr "Atalhos de visualização" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:193 +msgid "Move current slider thumb Up" +msgstr "Mover a barra de rolagem para cima" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:194 +msgid "Move current slider thumb Down" +msgstr "Mover a barra de rolagem para baixo" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:195 +msgid "Arrow Left" +msgstr "Seta esquerda" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:195 +msgid "Set upper thumb to current slider thumb" +msgstr "Definir o polegar superior para o polegar deslizante atual" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:196 +msgid "Arrow Right" +msgstr "Seta direita" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:196 +msgid "Set lower thumb to current slider thumb" +msgstr "Definir o polegar inferior para o polegar deslizante atual" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:197 +msgid "Add color change marker for current layer" +msgstr "Adicionar mudança de cor para a camada atual" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:198 +msgid "Delete color change marker for current layer" +msgstr "Excluir mudança de cor para a camada atual" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:200 +msgid "Layers Slider Shortcuts" +msgstr "Atalhos da barra de rolagem de camadas" + +#: src/slic3r/GUI/MainFrame.cpp:64 +msgid "" +" - Remember to check for updates at http://github.com/prusa3d/PrusaSlicer/" +"releases" +msgstr "" +" - Lembre-se de verificar por atualizações em http://github.com/prusa3d/" +"PrusaSlicer/releases" + +#: src/slic3r/GUI/MainFrame.cpp:159 +msgid "based on Slic3r" +msgstr "baseado no Slic3r" + +#: src/slic3r/GUI/MainFrame.cpp:189 +msgid "Plater" +msgstr "Prato" + +#: src/slic3r/GUI/MainFrame.cpp:400 +msgid "&New Project" +msgstr "&Novo projeto" + +#: src/slic3r/GUI/MainFrame.cpp:400 +msgid "Start a new project" +msgstr "Começar um novo projeto" + +#: src/slic3r/GUI/MainFrame.cpp:403 +msgid "&Open Project" +msgstr "&Abrir projeto" + +#: src/slic3r/GUI/MainFrame.cpp:403 +msgid "Open a project file" +msgstr "Abrir novo projeto" + +#: src/slic3r/GUI/MainFrame.cpp:408 +msgid "Recent projects" +msgstr "Projetos recentes" + +#: src/slic3r/GUI/MainFrame.cpp:417 +msgid "The selected project is no more available" +msgstr "O projeto selecionado não está mais disponível" + +#: src/slic3r/GUI/MainFrame.cpp:417 src/slic3r/GUI/MainFrame.cpp:755 +#: src/slic3r/GUI/PrintHostDialogs.cpp:231 +msgid "Error" +msgstr "Erro" + +#: src/slic3r/GUI/MainFrame.cpp:441 +msgid "&Save Project" +msgstr "&Salvar projeto" + +#: src/slic3r/GUI/MainFrame.cpp:441 +msgid "Save current project file" +msgstr "Salvar arquivo" + +#: src/slic3r/GUI/MainFrame.cpp:445 src/slic3r/GUI/MainFrame.cpp:447 +msgid "Save Project &as" +msgstr "Salvar projeto &como" + +#: src/slic3r/GUI/MainFrame.cpp:445 src/slic3r/GUI/MainFrame.cpp:447 +msgid "Save current project file as" +msgstr "Salvar arquivo atual como" + +#: src/slic3r/GUI/MainFrame.cpp:455 +msgid "Import STL/OBJ/AM&F/3MF" +msgstr "Import STL/OBJ/AM&F/3MF" + +#: src/slic3r/GUI/MainFrame.cpp:455 +msgid "Load a model" +msgstr "Carregar um modelo" + +#: src/slic3r/GUI/MainFrame.cpp:459 +msgid "Import &Config" +msgstr "Importar &config." + +#: src/slic3r/GUI/MainFrame.cpp:459 +msgid "Load exported configuration file" +msgstr "Carregar config. de arquivo exportado" + +#: src/slic3r/GUI/MainFrame.cpp:461 +msgid "Import Config from &project" +msgstr "Importar Config do &projeto" + +#: src/slic3r/GUI/MainFrame.cpp:461 +msgid "Load configuration from project file" +msgstr "Carregar config. de arquivo de projeto" + +#: src/slic3r/GUI/MainFrame.cpp:464 +msgid "Import Config &Bundle" +msgstr "Importar coleção &de config." + +#: src/slic3r/GUI/MainFrame.cpp:464 +msgid "Load presets from a bundle" +msgstr "Carregar predefinições de um pacote" + +#: src/slic3r/GUI/MainFrame.cpp:466 +msgid "&Import" +msgstr "&Importar" + +#: src/slic3r/GUI/MainFrame.cpp:469 src/slic3r/GUI/MainFrame.cpp:719 +msgid "Export &G-code" +msgstr "Exportar &G-code" + +#: src/slic3r/GUI/MainFrame.cpp:469 +msgid "Export current plate as G-code" +msgstr "Exporte o prato atual como o G-code" + +#: src/slic3r/GUI/MainFrame.cpp:473 src/slic3r/GUI/MainFrame.cpp:720 +msgid "S&end G-code" +msgstr "E&nviar G-code" + +#: src/slic3r/GUI/MainFrame.cpp:473 +msgid "Send to print current plate as G-code" +msgstr "Enviar para imprimir prato atual como G-code" + +#: src/slic3r/GUI/MainFrame.cpp:478 +msgid "Export plate as &STL" +msgstr "Exportar prato como &STL" + +#: src/slic3r/GUI/MainFrame.cpp:478 +msgid "Export current plate as STL" +msgstr "Exporte o prato atual como STL" + +#: src/slic3r/GUI/MainFrame.cpp:481 +msgid "Export plate as STL &including supports" +msgstr "Exportar prato como STL &incluindo suportes" + +#: src/slic3r/GUI/MainFrame.cpp:481 +msgid "Export current plate as STL including supports" +msgstr "Exporte o prato atual como o STL que inclui suportes" + +#: src/slic3r/GUI/MainFrame.cpp:484 +msgid "Export plate as &AMF" +msgstr "Exportar prato como &AMF" + +#: src/slic3r/GUI/MainFrame.cpp:484 +msgid "Export current plate as AMF" +msgstr "Exporte o prato atual como o AMF" + +#: src/slic3r/GUI/MainFrame.cpp:488 +msgid "Export &toolpaths as OBJ" +msgstr "Exportar &percurso da ferramenta como OBJ" + +#: src/slic3r/GUI/MainFrame.cpp:488 +msgid "Export toolpaths as OBJ" +msgstr "Exportar percursos como OBJ" + +#: src/slic3r/GUI/MainFrame.cpp:492 +msgid "Export &Config" +msgstr "Exportar &config." + +#: src/slic3r/GUI/MainFrame.cpp:492 +msgid "Export current configuration to file" +msgstr "Exporte a config. atual para o arquivo" + +#: src/slic3r/GUI/MainFrame.cpp:494 +msgid "Export Config &Bundle" +msgstr "Exportar coleção &de config." + +#: src/slic3r/GUI/MainFrame.cpp:494 +msgid "Export all presets to file" +msgstr "Exporte todas as predefinições para o arquivo" + +#: src/slic3r/GUI/MainFrame.cpp:496 +msgid "&Export" +msgstr "&Exportar" + +#: src/slic3r/GUI/MainFrame.cpp:502 +msgid "Quick Slice" +msgstr "Fatiamento rápido" + +#: src/slic3r/GUI/MainFrame.cpp:502 +msgid "Slice a file into a G-code" +msgstr "Fatiar um arquivo em um G-code" + +#: src/slic3r/GUI/MainFrame.cpp:508 +msgid "Quick Slice and Save As" +msgstr "Salvamento rápido e salvar como" + +#: src/slic3r/GUI/MainFrame.cpp:508 +msgid "Slice a file into a G-code, save as" +msgstr "Fatiar um arquivo em um G-code, salvar como" + +#: src/slic3r/GUI/MainFrame.cpp:514 +msgid "Repeat Last Quick Slice" +msgstr "Repetir Último Fatiamento Rápido" + +#: src/slic3r/GUI/MainFrame.cpp:514 +msgid "Repeat last quick slice" +msgstr "Repetir último fatiamento rápido" + +#: src/slic3r/GUI/MainFrame.cpp:522 +msgid "(Re)Slice No&w" +msgstr "(Re)Fatiar ago&ra" + +#: src/slic3r/GUI/MainFrame.cpp:522 +msgid "Start new slicing process" +msgstr "Começar novo processo de fatiamento" + +#: src/slic3r/GUI/MainFrame.cpp:526 +msgid "&Repair STL file" +msgstr "&Reparar arquivo STL" + +#: src/slic3r/GUI/MainFrame.cpp:526 +msgid "Automatically repair an STL file" +msgstr "Reparar automaticamente um arquivo STL" + +#: src/slic3r/GUI/MainFrame.cpp:529 +msgid "&Quit" +msgstr "&Sair" + +#: src/slic3r/GUI/MainFrame.cpp:529 +#, c-format +msgid "Quit %s" +msgstr "Sair %s" + +#: src/slic3r/GUI/MainFrame.cpp:554 +msgid "&Select all" +msgstr "&Selecionar todos" + +#: src/slic3r/GUI/MainFrame.cpp:555 +msgid "Selects all objects" +msgstr "Selecionar todos os objetos" + +#: src/slic3r/GUI/MainFrame.cpp:557 +msgid "D&eselect all" +msgstr "D&eselecionar todos" + +#: src/slic3r/GUI/MainFrame.cpp:558 +msgid "Deselects all objects" +msgstr "Deselecionar todos os objetos" + +#: src/slic3r/GUI/MainFrame.cpp:561 +msgid "&Delete selected" +msgstr "&Excluir seleção" + +#: src/slic3r/GUI/MainFrame.cpp:562 +msgid "Deletes the current selection" +msgstr "Excluir a seleção atual" + +#: src/slic3r/GUI/MainFrame.cpp:564 +msgid "Delete &all" +msgstr "Excluir &todos" + +#: src/slic3r/GUI/MainFrame.cpp:565 +msgid "Deletes all objects" +msgstr "Excluir todos os objetos" + +#: src/slic3r/GUI/MainFrame.cpp:569 +msgid "&Undo" +msgstr "&Desfazer" + +#: src/slic3r/GUI/MainFrame.cpp:572 +msgid "&Redo" +msgstr "&Refazer" + +#: src/slic3r/GUI/MainFrame.cpp:577 +msgid "&Copy" +msgstr "&Copiar" + +#: src/slic3r/GUI/MainFrame.cpp:578 +msgid "Copy selection to clipboard" +msgstr "Copiar seleção para a área de transferência" + +#: src/slic3r/GUI/MainFrame.cpp:580 +msgid "&Paste" +msgstr "&Colar" + +#: src/slic3r/GUI/MainFrame.cpp:581 +msgid "Paste clipboard" +msgstr "Colar área de transferência" + +#: src/slic3r/GUI/MainFrame.cpp:590 +msgid "&Plater Tab" +msgstr "&Prato" + +#: src/slic3r/GUI/MainFrame.cpp:590 +msgid "Show the plater" +msgstr "Mostrar o prato" + +#: src/slic3r/GUI/MainFrame.cpp:597 +msgid "P&rint Settings Tab" +msgstr "C&onfig. de impressão" + +#: src/slic3r/GUI/MainFrame.cpp:597 +msgid "Show the print settings" +msgstr "Mostrar as config. de impressão" + +#: src/slic3r/GUI/MainFrame.cpp:599 src/slic3r/GUI/MainFrame.cpp:722 +msgid "&Filament Settings Tab" +msgstr "&config. de filamentos" + +#: src/slic3r/GUI/MainFrame.cpp:599 +msgid "Show the filament settings" +msgstr "Mostrar as config. de filamento" + +#: src/slic3r/GUI/MainFrame.cpp:602 +msgid "Print&er Settings Tab" +msgstr "A&ba de config. da impressora" + +#: src/slic3r/GUI/MainFrame.cpp:602 +msgid "Show the printer settings" +msgstr "Mostrar as config. da impressora" + +#: src/slic3r/GUI/MainFrame.cpp:606 +msgid "3&D" +msgstr "3&D" + +#: src/slic3r/GUI/MainFrame.cpp:606 +msgid "Show the 3D editing view" +msgstr "Mostrar a vista de edição 3D" + +#: src/slic3r/GUI/MainFrame.cpp:609 +msgid "Pre&view" +msgstr "Pre&visualização" + +#: src/slic3r/GUI/MainFrame.cpp:609 +msgid "Show the 3D slices preview" +msgstr "Mostrar a pré-visualização do fatiamento 3D" + +#: src/slic3r/GUI/MainFrame.cpp:628 +msgid "Print &Host Upload Queue" +msgstr "Imprimir &Fila de upload do Host" + +#: src/slic3r/GUI/MainFrame.cpp:628 +msgid "Display the Print Host Upload Queue window" +msgstr "Exibir a janela fila de upload do host de impressão" + +#: src/slic3r/GUI/MainFrame.cpp:637 +msgid "Iso" +msgstr "Isométrico" + +#: src/slic3r/GUI/MainFrame.cpp:637 +msgid "Iso View" +msgstr "Vista isométrica" + +#. TRN To be shown in the main menu View->Top +#. TRN To be shown in Print Settings "Top solid layers" +#: src/slic3r/GUI/MainFrame.cpp:641 src/libslic3r/PrintConfig.cpp:2094 +msgid "Top" +msgstr "Topo" + +#: src/slic3r/GUI/MainFrame.cpp:641 +msgid "Top View" +msgstr "Vista do topo" + +#. TRN To be shown in the main menu View->Bottom +#. TRN To be shown in Print Settings "Bottom solid layers" +#: src/slic3r/GUI/MainFrame.cpp:644 src/libslic3r/PrintConfig.cpp:159 +msgid "Bottom" +msgstr "Base" + +#: src/slic3r/GUI/MainFrame.cpp:644 +msgid "Bottom View" +msgstr "Vista da base" + +#: src/slic3r/GUI/MainFrame.cpp:646 +msgid "Front" +msgstr "Frente" + +#: src/slic3r/GUI/MainFrame.cpp:646 +msgid "Front View" +msgstr "Vista da frente" + +#: src/slic3r/GUI/MainFrame.cpp:648 src/libslic3r/PrintConfig.cpp:1611 +msgid "Rear" +msgstr "Traseira" + +#: src/slic3r/GUI/MainFrame.cpp:648 +msgid "Rear View" +msgstr "Vista traseira" + +#: src/slic3r/GUI/MainFrame.cpp:650 +msgid "Left" +msgstr "Esquerda" + +#: src/slic3r/GUI/MainFrame.cpp:650 +msgid "Left View" +msgstr "Vista esquerda" + +#: src/slic3r/GUI/MainFrame.cpp:652 +msgid "Right" +msgstr "Direita" + +#: src/slic3r/GUI/MainFrame.cpp:652 +msgid "Right View" +msgstr "Vista direita" + +#: src/slic3r/GUI/MainFrame.cpp:659 +msgid "Prusa 3D &Drivers" +msgstr "Drivers 3D &Prusa" + +#: src/slic3r/GUI/MainFrame.cpp:659 +msgid "Open the Prusa3D drivers download page in your browser" +msgstr "Abrir a página para baixar os drivers da Prusa3D no seu navegador" + +#: src/slic3r/GUI/MainFrame.cpp:661 +msgid "Software &Releases" +msgstr "Lançamentos de &software" + +#: src/slic3r/GUI/MainFrame.cpp:661 +msgid "Open the software releases page in your browser" +msgstr "Abrir a página de lançamentos de software no seu navegador" + +#: src/slic3r/GUI/MainFrame.cpp:667 +#, c-format +msgid "%s &Website" +msgstr "%s &Site" + +#: src/slic3r/GUI/MainFrame.cpp:668 +#, c-format +msgid "Open the %s website in your browser" +msgstr "Abra o site do %s no seu navegador" + +#: src/slic3r/GUI/MainFrame.cpp:674 +msgid "System &Info" +msgstr "Informação &do sistema" + +#: src/slic3r/GUI/MainFrame.cpp:674 +msgid "Show system information" +msgstr "Mostrar a informação do sistema" + +#: src/slic3r/GUI/MainFrame.cpp:676 +msgid "Show &Configuration Folder" +msgstr "Mostrar &pasta de config." + +#: src/slic3r/GUI/MainFrame.cpp:676 +msgid "Show user configuration folder (datadir)" +msgstr "Mostrar pasta de config. do usuário (datadir)" + +#: src/slic3r/GUI/MainFrame.cpp:678 +msgid "Report an I&ssue" +msgstr "Reportar um p&roblema" + +#: src/slic3r/GUI/MainFrame.cpp:678 +#, c-format +msgid "Report an issue on %s" +msgstr "Relatar um problema em %s" + +#: src/slic3r/GUI/MainFrame.cpp:680 +#, c-format +msgid "&About %s" +msgstr "&Sobre %s" + +#: src/slic3r/GUI/MainFrame.cpp:680 +msgid "Show about dialog" +msgstr "Mostrar diálogo sobre" + +#: src/slic3r/GUI/MainFrame.cpp:683 +msgid "Show the list of the keyboard shortcuts" +msgstr "Mostrar lista dos atalhos no teclado" + +#: src/slic3r/GUI/MainFrame.cpp:691 +msgid "&File" +msgstr "&Arquivo" + +#: src/slic3r/GUI/MainFrame.cpp:692 +msgid "&Edit" +msgstr "&Editar" + +#: src/slic3r/GUI/MainFrame.cpp:693 +msgid "&Window" +msgstr "&Janela" + +#: src/slic3r/GUI/MainFrame.cpp:694 +msgid "&View" +msgstr "&Vista" + +#: src/slic3r/GUI/MainFrame.cpp:697 +msgid "&Help" +msgstr "&Ajuda" + +#: src/slic3r/GUI/MainFrame.cpp:719 +msgid "E&xport" +msgstr "E&xportar" + +#: src/slic3r/GUI/MainFrame.cpp:720 +msgid "S&end to print" +msgstr "E&nviar para impressora" + +#: src/slic3r/GUI/MainFrame.cpp:722 +msgid "Mate&rial Settings Tab" +msgstr "A&ba de config. de material" + +#: src/slic3r/GUI/MainFrame.cpp:743 +msgid "Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):" +msgstr "Escolha um arquivo para fatiar (STL/OBJ/AMF/3MF/PRUSA):" + +#: src/slic3r/GUI/MainFrame.cpp:754 +msgid "No previously sliced file." +msgstr "Sem arquivo fatiado anteriormente." + +#: src/slic3r/GUI/MainFrame.cpp:760 +msgid "Previously sliced file (" +msgstr "Arquivo fatiado anteriormente (" + +#: src/slic3r/GUI/MainFrame.cpp:760 +msgid ") not found." +msgstr ") não encontrado." + +#: src/slic3r/GUI/MainFrame.cpp:761 +msgid "File Not Found" +msgstr "Arquivo não encontrado" + +#: src/slic3r/GUI/MainFrame.cpp:796 +#, c-format +msgid "Save %s file as:" +msgstr "Salve o arquivo %s como:" + +#: src/slic3r/GUI/MainFrame.cpp:796 +msgid "SVG" +msgstr "SVG" + +#: src/slic3r/GUI/MainFrame.cpp:796 +msgid "G-code" +msgstr "G-code" + +#: src/slic3r/GUI/MainFrame.cpp:808 +msgid "Save zip file as:" +msgstr "Salvar arquivo compactado(zip) como:" + +#: src/slic3r/GUI/MainFrame.cpp:817 src/slic3r/GUI/Plater.cpp:2981 +#: src/slic3r/GUI/Plater.cpp:4533 src/slic3r/GUI/Tab.cpp:1194 +#: src/slic3r/GUI/Tab.cpp:3786 +msgid "Slicing" +msgstr "Fatiamento" + +#. TRN "Processing input_file_basename" +#: src/slic3r/GUI/MainFrame.cpp:819 +#, c-format +msgid "Processing %s" +msgstr "Processando %s" + +#: src/slic3r/GUI/MainFrame.cpp:842 +msgid " was successfully sliced." +msgstr " foi fatiado com sucesso." + +#: src/slic3r/GUI/MainFrame.cpp:844 +msgid "Slicing Done!" +msgstr "Fatiamento completo!" + +#: src/slic3r/GUI/MainFrame.cpp:859 +msgid "Select the STL file to repair:" +msgstr "Selecione o arquivo STL para corrigir:" + +#: src/slic3r/GUI/MainFrame.cpp:869 +msgid "Save OBJ file (less prone to coordinate errors than STL) as:" +msgstr "" +"Salvar arquivo OBJ (menos propenso a erros de coordenada que STL) como:" + +#: src/slic3r/GUI/MainFrame.cpp:881 +msgid "Your file was repaired." +msgstr "Seu arquivo foi corrigido." + +#: src/slic3r/GUI/MainFrame.cpp:881 src/libslic3r/PrintConfig.cpp:3257 +msgid "Repair" +msgstr "Corrigir" + +#: src/slic3r/GUI/MainFrame.cpp:895 +msgid "Save configuration as:" +msgstr "Salvar config. como:" + +#: src/slic3r/GUI/MainFrame.cpp:914 src/slic3r/GUI/MainFrame.cpp:976 +msgid "Select configuration to load:" +msgstr "Selecionar config. para carregar:" + +#: src/slic3r/GUI/MainFrame.cpp:950 +msgid "Save presets bundle as:" +msgstr "Salvar pacote de predefinições como:" + +#: src/slic3r/GUI/MainFrame.cpp:997 +#, c-format +msgid "%d presets successfully imported." +msgstr "%d predefinições importadas com êxito." + +#: src/slic3r/GUI/MsgDialog.cpp:73 +#, c-format +msgid "%s error" +msgstr "%s erro" + +#: src/slic3r/GUI/MsgDialog.cpp:74 +#, c-format +msgid "%s has encountered an error" +msgstr "%s encontrou um erro" + +#: src/slic3r/GUI/OptionsGroup.cpp:249 +msgctxt "Layers" +msgid "Top" +msgstr "Topo" + +#: src/slic3r/GUI/OptionsGroup.cpp:249 +msgctxt "Layers" +msgid "Bottom" +msgstr "Base" + +#: src/slic3r/GUI/Plater.cpp:146 +msgid "Volume" +msgstr "Volume" + +#: src/slic3r/GUI/Plater.cpp:147 +msgid "Facets" +msgstr "Facetas" + +#: src/slic3r/GUI/Plater.cpp:148 +msgid "Materials" +msgstr "Materiais" + +#: src/slic3r/GUI/Plater.cpp:151 +msgid "Manifold" +msgstr "Múltiplo" + +#: src/slic3r/GUI/Plater.cpp:201 +msgid "Sliced Info" +msgstr "Informações fatiadas" + +#: src/slic3r/GUI/Plater.cpp:220 src/slic3r/GUI/Plater.cpp:1150 +msgid "Used Filament (m)" +msgstr "Filamento utilizado (m)" + +#: src/slic3r/GUI/Plater.cpp:221 +msgid "Used Filament (mm³)" +msgstr "Filamento utilizado (mm³)" + +#: src/slic3r/GUI/Plater.cpp:222 +msgid "Used Filament (g)" +msgstr "Filamento utilizado (g)" + +#: src/slic3r/GUI/Plater.cpp:223 +msgid "Used Material (unit)" +msgstr "Material utilizado (unidade)" + +#: src/slic3r/GUI/Plater.cpp:224 src/slic3r/GUI/Plater.cpp:1165 +#: src/libslic3r/PrintConfig.cpp:742 +msgid "Cost" +msgstr "Custo" + +#: src/slic3r/GUI/Plater.cpp:225 src/slic3r/GUI/Plater.cpp:1137 +#: src/slic3r/GUI/Plater.cpp:1179 +msgid "Estimated printing time" +msgstr "Tempo estimado de impressão" + +#: src/slic3r/GUI/Plater.cpp:226 +msgid "Number of tool changes" +msgstr "Número de mudanças de ferramenta" + +#: src/slic3r/GUI/Plater.cpp:316 +msgid "Click to edit preset" +msgstr "Clique para editar a predefinição" + +#: src/slic3r/GUI/Plater.cpp:468 +msgid "Select what kind of support do you need" +msgstr "Selecione o tipo de suporte que você precisa" + +#: src/slic3r/GUI/Plater.cpp:470 src/libslic3r/PrintConfig.cpp:1865 +#: src/libslic3r/PrintConfig.cpp:2561 +msgid "Support on build plate only" +msgstr "Suportes somente na mesa de impressão" + +#: src/slic3r/GUI/Plater.cpp:471 src/slic3r/GUI/Plater.cpp:592 +msgid "For support enforcers only" +msgstr "Para apenas reforçadores de suporte" + +#: src/slic3r/GUI/Plater.cpp:472 +msgid "Everywhere" +msgstr "Em toda parte" + +#: src/slic3r/GUI/Plater.cpp:504 src/slic3r/GUI/Tab.cpp:1091 +msgid "Brim" +msgstr "Aba" + +#: src/slic3r/GUI/Plater.cpp:506 +msgid "" +"This flag enables the brim that will be printed around each object on the " +"first layer." +msgstr "" +"Este sinalizador permite que a aba que será impressa em torno de cada objeto " +"na primeira camada." + +#: src/slic3r/GUI/Plater.cpp:514 +msgid "Purging volumes" +msgstr "Volumes de purga" + +#: src/slic3r/GUI/Plater.cpp:606 +msgid "Select what kind of pad do you need" +msgstr "Selecione o tipo de bloco que você precisa" + +#: src/slic3r/GUI/Plater.cpp:608 +msgid "Below object" +msgstr "Abaixo do objeto" + +#: src/slic3r/GUI/Plater.cpp:609 +msgid "Around object" +msgstr "Em torno do objeto" + +#: src/slic3r/GUI/Plater.cpp:781 +msgid "Print settings" +msgstr "Config. de impressão" + +#: src/slic3r/GUI/Plater.cpp:782 src/slic3r/GUI/Tab.cpp:1640 +#: src/slic3r/GUI/Tab.cpp:1641 +msgid "Filament" +msgstr "Filamento" + +#: src/slic3r/GUI/Plater.cpp:783 +msgid "SLA print settings" +msgstr "Config. de impressão de SLA" + +#: src/slic3r/GUI/Plater.cpp:784 src/slic3r/GUI/Preset.cpp:1314 +msgid "SLA material" +msgstr "Material de SLA" + +#: src/slic3r/GUI/Plater.cpp:785 +msgid "Printer" +msgstr "Impressora" + +#: src/slic3r/GUI/Plater.cpp:835 src/slic3r/GUI/Plater.cpp:4823 +msgid "Send to printer" +msgstr "Enviar para a impressora" + +#: src/slic3r/GUI/Plater.cpp:838 src/slic3r/GUI/Plater.cpp:2981 +#: src/slic3r/GUI/Plater.cpp:4536 +msgid "Slice now" +msgstr "Fatiar agora" + +#: src/slic3r/GUI/Plater.cpp:978 +msgid "Hold Shift to Slice & Export G-code" +msgstr "Hold Shift to Slice & Export G-code" + +#: src/slic3r/GUI/Plater.cpp:1083 +#, c-format +msgid "%d (%d shells)" +msgstr "%d (%d paredes)" + +#: src/slic3r/GUI/Plater.cpp:1088 +#, c-format +msgid "Auto-repaired (%d errors)" +msgstr "Auto reparando (%d erros):" + +#: src/slic3r/GUI/Plater.cpp:1091 +#, c-format +msgid "" +"%d degenerate facets, %d edges fixed, %d facets removed, %d facets added, %d " +"facets reversed, %d backwards edges" +msgstr "" +"%d degenerate facets, %d edges fixed, %d facets removed, %d facets added, %d " +"facets reversed, %d backwards edges" + +#: src/slic3r/GUI/Plater.cpp:1101 +msgid "Yes" +msgstr "Sim" + +#: src/slic3r/GUI/Plater.cpp:1124 +msgid "Used Material (ml)" +msgstr "Material usado (ml)" + +#: src/slic3r/GUI/Plater.cpp:1127 +msgid "object(s)" +msgstr "objeto(s)" + +#: src/slic3r/GUI/Plater.cpp:1127 +msgid "supports and pad" +msgstr "suportes e bloco" + +#: src/slic3r/GUI/Plater.cpp:1152 src/slic3r/GUI/Plater.cpp:1167 +msgid "objects" +msgstr "objetos" + +#: src/slic3r/GUI/Plater.cpp:1152 src/slic3r/GUI/Plater.cpp:1167 +msgid "wipe tower" +msgstr "torre de limpeza" + +#: src/slic3r/GUI/Plater.cpp:1182 +msgid "normal mode" +msgstr "modo normal" + +#: src/slic3r/GUI/Plater.cpp:1186 src/slic3r/GUI/Plater.cpp:1195 +#: src/libslic3r/PrintConfig.cpp:565 +msgid "Color" +msgstr "Cor" + +#: src/slic3r/GUI/Plater.cpp:1191 +msgid "stealth mode" +msgstr "modo silencioso" + +#: src/slic3r/GUI/Plater.cpp:1286 +msgid "Load File" +msgstr "Carregar arquivo" + +#: src/slic3r/GUI/Plater.cpp:1290 +msgid "Load Files" +msgstr "Carregar arquivos" + +#: src/slic3r/GUI/Plater.cpp:1519 +msgid "ERROR: not enough resources to execute a new job." +msgstr "ERRO: não há recursos suficientes para executar um novo trabalho." + +#: src/slic3r/GUI/Plater.cpp:2089 +msgid "New Project" +msgstr "Novo projeto" + +#: src/slic3r/GUI/Plater.cpp:2206 +msgid "Loading" +msgstr "Carregando" + +#: src/slic3r/GUI/Plater.cpp:2216 +#, c-format +msgid "Processing input file %s\n" +msgstr "Processando o arquivo de entrada %s\n" + +#: src/slic3r/GUI/Plater.cpp:2244 +msgid "" +"You can't load SLA project if there is at least one multi-part object on the " +"bed" +msgstr "" +"Não é possível carregar o projeto de SLA se houver pelo menos um objeto de " +"várias partes na mesa" + +#: src/slic3r/GUI/Plater.cpp:2245 src/slic3r/GUI/Tab.cpp:3146 +msgid "Please check your object list before preset changing." +msgstr "Verifique a lista de objetos antes de alterar a predefinição." + +#: src/slic3r/GUI/Plater.cpp:2288 +msgid "" +"This file contains several objects positioned at multiple heights. Instead " +"of considering them as multiple objects, should I consider\n" +"this file as a single object having multiple parts?\n" +msgstr "" +"Este arquivo contém vários objetos posicionados em várias alturas. Em vez de " +"considerá-los como múltiplos objetos, devo considerar\n" +"Este arquivo como um único objeto com várias partes?\n" + +#: src/slic3r/GUI/Plater.cpp:2291 src/slic3r/GUI/Plater.cpp:2343 +msgid "Multi-part object detected" +msgstr "Objeto de várias partes detectado" + +#: src/slic3r/GUI/Plater.cpp:2298 +msgid "" +"This file cannot be loaded in a simple mode. Do you want to switch to an " +"advanced mode?\n" +msgstr "" +"Este arquivo não pode ser carregado em um modo simples. Deseja mudar para um " +"modo avançado?\n" + +#: src/slic3r/GUI/Plater.cpp:2299 +msgid "Detected advanced data" +msgstr "Dados avançados detectados" + +#: src/slic3r/GUI/Plater.cpp:2320 +#, c-format +msgid "" +"You can't to add the object(s) from %s because of one or some of them " +"is(are) multi-part" +msgstr "" +"Você não pode adicionar o objeto (s) %s por causa de um ou alguns deles é " +"(são) de várias partes" + +#: src/slic3r/GUI/Plater.cpp:2340 +msgid "" +"Multiple objects were loaded for a multi-material printer.\n" +"Instead of considering them as multiple objects, should I consider\n" +"these files to represent a single object having multiple parts?\n" +msgstr "" +"Vários objetos foram carregados para uma impressora de vários materiais.\n" +"Em vez de considerá-los como múltiplos objetos, devo considerar\n" +"esses arquivos para representar um único objeto com várias partes?\n" + +#: src/slic3r/GUI/Plater.cpp:2356 +msgid "Loaded" +msgstr "Carregado" + +#: src/slic3r/GUI/Plater.cpp:2458 +msgid "" +"Your object appears to be too large, so it was automatically scaled down to " +"fit your print bed." +msgstr "" +"Seu objeto parece ser muito grande, por isso foi automaticamente " +"dimensionado para baixo para caber sua mesa de impressão." + +#: src/slic3r/GUI/Plater.cpp:2459 +msgid "Object too large?" +msgstr "Objeto muito grande?" + +#: src/slic3r/GUI/Plater.cpp:2517 +msgid "Export STL file:" +msgstr "Exportar arquivo STL:" + +#: src/slic3r/GUI/Plater.cpp:2524 +msgid "Export AMF file:" +msgstr "Exportar arquivo AMF:" + +#: src/slic3r/GUI/Plater.cpp:2530 +msgid "Save file as:" +msgstr "Salvar arquivo como:" + +#: src/slic3r/GUI/Plater.cpp:2536 +msgid "Export OBJ file:" +msgstr "Exportar arquivo OBJ:" + +#: src/slic3r/GUI/Plater.cpp:2638 +msgid "Delete Object" +msgstr "Excluir objeto" + +#: src/slic3r/GUI/Plater.cpp:2649 +msgid "Reset Project" +msgstr "Redefinir projeto" + +#: src/slic3r/GUI/Plater.cpp:2688 +msgid "Optimize Rotation" +msgstr "Otimize a rotação" + +#: src/slic3r/GUI/Plater.cpp:2734 +msgid "Arranging" +msgstr "Organizar" + +#: src/slic3r/GUI/Plater.cpp:2757 +msgid "Could not arrange model objects! Some geometries may be invalid." +msgstr "" +"Não foi possível organizar objetos de modelo! Algumas geometrias podem ser " +"inválidas." + +#: src/slic3r/GUI/Plater.cpp:2763 +msgid "Arranging canceled." +msgstr "Arranjo cancelado." + +#: src/slic3r/GUI/Plater.cpp:2764 +msgid "Arranging done." +msgstr "Arranjo feito." + +#: src/slic3r/GUI/Plater.cpp:2780 +msgid "Searching for optimal orientation" +msgstr "Procurando orientação ideal" + +#: src/slic3r/GUI/Plater.cpp:2813 +msgid "Orientation search canceled." +msgstr "Pesquisa de orientação cancelada." + +#: src/slic3r/GUI/Plater.cpp:2814 +msgid "Orientation found." +msgstr "Orientação encontrada." + +#: src/slic3r/GUI/Plater.cpp:2830 +msgid "" +"The selected object can't be split because it contains more than one volume/" +"material." +msgstr "" +"O objeto selecionado não pode ser dividido porque contém mais de um volume/" +"material." + +#: src/slic3r/GUI/Plater.cpp:2841 +msgid "Split to Objects" +msgstr "Dividir em objetos" + +#: src/slic3r/GUI/Plater.cpp:2966 +msgid "Invalid data" +msgstr "Dados inválidos" + +#: src/slic3r/GUI/Plater.cpp:2975 +msgid "Ready to slice" +msgstr "Pronto para fatiar" + +#: src/slic3r/GUI/Plater.cpp:3013 src/slic3r/GUI/PrintHostDialogs.cpp:232 +msgid "Cancelling" +msgstr "Cancelar" + +#: src/slic3r/GUI/Plater.cpp:3030 +msgid "Another export job is currently running." +msgstr "Outro trabalho de exportação está em execução no momento." + +#: src/slic3r/GUI/Plater.cpp:3084 src/slic3r/GUI/Plater.cpp:3549 +msgid "Reload from Disk" +msgstr "Recarregar a partir do disco" + +#: src/slic3r/GUI/Plater.cpp:3120 +msgid "Fix Throught NetFabb" +msgstr "Arrumar através do NetFabb" + +#: src/slic3r/GUI/Plater.cpp:3307 +msgid "Export failed" +msgstr "Falha na exportação" + +#: src/slic3r/GUI/Plater.cpp:3312 src/slic3r/GUI/PrintHostDialogs.cpp:233 +msgid "Cancelled" +msgstr "Cancelado" + +#: src/slic3r/GUI/Plater.cpp:3520 src/slic3r/GUI/Plater.cpp:3539 +msgid "Remove the selected object" +msgstr "Remover o objeto selecionado" + +#: src/slic3r/GUI/Plater.cpp:3526 +msgid "Add one more instance of the selected object" +msgstr "Adicionar mais uma instância do objeto selecionado" + +#: src/slic3r/GUI/Plater.cpp:3528 +msgid "Remove one instance of the selected object" +msgstr "Remover uma instância do objeto selecionado" + +#: src/slic3r/GUI/Plater.cpp:3530 +msgid "Set number of instances" +msgstr "Definir o número de instâncias" + +#: src/slic3r/GUI/Plater.cpp:3530 +msgid "Change the number of instances of the selected object" +msgstr "Alterar o número de instâncias do objeto selecionado" + +#: src/slic3r/GUI/Plater.cpp:3549 +msgid "Reload the selected file from Disk" +msgstr "Recarregar o arquivo selecionado a partir do disco" + +#: src/slic3r/GUI/Plater.cpp:3552 +msgid "Export the selected object as STL file" +msgstr "Exportar o objeto selecionado como arquivo STL" + +#: src/slic3r/GUI/Plater.cpp:3577 +msgid "Along X axis" +msgstr "Ao longo do eixo X" + +#: src/slic3r/GUI/Plater.cpp:3577 +msgid "Mirror the selected object along the X axis" +msgstr "Espelhar o objeto selecionado ao longo do eixo X" + +#: src/slic3r/GUI/Plater.cpp:3579 +msgid "Along Y axis" +msgstr "Ao longo do eixo Y" + +#: src/slic3r/GUI/Plater.cpp:3579 +msgid "Mirror the selected object along the Y axis" +msgstr "Espelhar o objeto selecionado ao longo do eixo Y" + +#: src/slic3r/GUI/Plater.cpp:3581 +msgid "Along Z axis" +msgstr "Ao longo do eixo Z" + +#: src/slic3r/GUI/Plater.cpp:3581 +msgid "Mirror the selected object along the Z axis" +msgstr "Espelhar o objeto selecionado ao longo do eixo Z" + +#: src/slic3r/GUI/Plater.cpp:3584 +msgid "Mirror" +msgstr "Espelhar" + +#: src/slic3r/GUI/Plater.cpp:3584 +msgid "Mirror the selected object" +msgstr "Espelhar o objeto selecionado" + +#: src/slic3r/GUI/Plater.cpp:3596 +msgid "To objects" +msgstr "Para objetos" + +#: src/slic3r/GUI/Plater.cpp:3596 src/slic3r/GUI/Plater.cpp:3616 +msgid "Split the selected object into individual objects" +msgstr "Dividir o objeto selecionado em objetos individuais" + +#: src/slic3r/GUI/Plater.cpp:3598 +msgid "To parts" +msgstr "Para peças" + +#: src/slic3r/GUI/Plater.cpp:3598 src/slic3r/GUI/Plater.cpp:3630 +msgid "Split the selected object into individual sub-parts" +msgstr "Dividir o objeto selecionado em subpartes individuais" + +#: src/slic3r/GUI/Plater.cpp:3601 src/slic3r/GUI/Plater.cpp:3616 +#: src/slic3r/GUI/Plater.cpp:3630 src/libslic3r/PrintConfig.cpp:3281 +msgid "Split" +msgstr "Dividir" + +#: src/slic3r/GUI/Plater.cpp:3601 +msgid "Split the selected object" +msgstr "Dividir o objeto selecionado" + +#: src/slic3r/GUI/Plater.cpp:3622 +msgid "Optimize orientation" +msgstr "Otimize a orientação" + +#: src/slic3r/GUI/Plater.cpp:3622 +msgid "Optimize the rotation of the object for better print results." +msgstr "" +"Otimize a rotação do objeto para obter melhores resultados de impressão." + +#: src/slic3r/GUI/Plater.cpp:3662 +msgid "3D editor view" +msgstr "vista do editor 3D" + +#: src/slic3r/GUI/Plater.cpp:3670 src/slic3r/GUI/Tab.cpp:2590 +msgid "Preview" +msgstr "Visualização" + +#: src/slic3r/GUI/Plater.cpp:3907 +msgid "" +"%1% printer was active at the time the target Undo / Redo snapshot was " +"taken. Switching to %1% printer requires reloading of %1% presets." +msgstr "" +"a impressora %1% estava ativa no momento em que a captura de desfazer/" +"refazer de destino foi tirado. Mudar para %1% impressora requer recarga de " +"%1% predefinições." + +#: src/slic3r/GUI/Plater.cpp:4081 +msgid "Load Project" +msgstr "Carregar projeto" + +#: src/slic3r/GUI/Plater.cpp:4109 +msgid "Import Object" +msgstr "Importar objeto" + +#: src/slic3r/GUI/Plater.cpp:4113 +msgid "Import Objects" +msgstr "Importar objetos" + +#: src/slic3r/GUI/Plater.cpp:4172 +msgid "All objects will be removed, continue ?" +msgstr "Todos os objetos serão removidos, continuar?" + +#: src/slic3r/GUI/Plater.cpp:4180 +msgid "Delete Selected Objects" +msgstr "Excluir objetos selecionados" + +#: src/slic3r/GUI/Plater.cpp:4188 +msgid "Increase Instances" +msgstr "Aumentar instâncias" + +#: src/slic3r/GUI/Plater.cpp:4224 +msgid "Decrease Instances" +msgstr "Diminuir instâncias" + +#: src/slic3r/GUI/Plater.cpp:4260 +#, c-format +msgid "Set numbers of copies to %d" +msgstr "Definir números de cópias para %d" + +#: src/slic3r/GUI/Plater.cpp:4290 +msgid "Cut by Plane" +msgstr "Cortado por plano" + +#: src/slic3r/GUI/Plater.cpp:4322 +msgid "Save G-code file as:" +msgstr "Salve o arquivo G-code como:" + +#: src/slic3r/GUI/Plater.cpp:4322 +msgid "Save SL1 file as:" +msgstr "Salvar SL1 arquivo como:" + +#: src/slic3r/GUI/Plater.cpp:4434 +#, c-format +msgid "STL file exported to %s" +msgstr "Arquivo STL exportado para %s" + +#: src/slic3r/GUI/Plater.cpp:4450 +#, c-format +msgid "AMF file exported to %s" +msgstr "Arquivo AMF exportado para %s" + +#: src/slic3r/GUI/Plater.cpp:4453 +#, c-format +msgid "Error exporting AMF file %s" +msgstr "Erro ao exportar arquivo AMF %s" + +#: src/slic3r/GUI/Plater.cpp:4479 +#, c-format +msgid "3MF file exported to %s" +msgstr "Arquivo 3MF exportado para %s" + +#: src/slic3r/GUI/Plater.cpp:4484 +#, c-format +msgid "Error exporting 3MF file %s" +msgstr "Erro ao exportar arquivo 3MF %s" + +#: src/slic3r/GUI/Plater.cpp:4822 +msgid "Export" +msgstr "Exportar" + +#: src/slic3r/GUI/Plater.cpp:4823 +msgid "Send G-code" +msgstr "Enviar G-code" + +#: src/slic3r/GUI/Plater.cpp:4907 +msgid "Paste From Clipboard" +msgstr "Colar da área de transferência" + +#: src/slic3r/GUI/Preferences.cpp:22 src/slic3r/GUI/Tab.cpp:2001 +#: src/slic3r/GUI/Tab.cpp:2242 +msgid "General" +msgstr "Geral" + +#: src/slic3r/GUI/Preferences.cpp:44 +msgid "Remember output directory" +msgstr "Lembrar diretório de saída" + +#: src/slic3r/GUI/Preferences.cpp:46 +msgid "" +"If this is enabled, Slic3r will prompt the last output directory instead of " +"the one containing the input files." +msgstr "" +"Se isso estiver habilitado, Slic3r solicitará o último diretório de saída em " +"vez de um contendo os arquivos de entrada." + +#: src/slic3r/GUI/Preferences.cpp:52 +msgid "Auto-center parts" +msgstr "Centrar automaticamente as partes" + +#: src/slic3r/GUI/Preferences.cpp:54 +msgid "" +"If this is enabled, Slic3r will auto-center objects around the print bed " +"center." +msgstr "" +"Se isso estiver habilitado, o Slic3r irá centralizar objetos automaticamente " +"ao redor do centro de mesa de impressão." + +#: src/slic3r/GUI/Preferences.cpp:60 +msgid "Background processing" +msgstr "Processamento em segundo plano" + +#: src/slic3r/GUI/Preferences.cpp:62 +msgid "" +"If this is enabled, Slic3r will pre-process objects as soon as they're " +"loaded in order to save time when exporting G-code." +msgstr "" +"Se isso estiver ativado, o Slic3r irá pré-processar objetos assim que eles " +"forem carregados para economizar tempo ao exportar o G-code." + +#: src/slic3r/GUI/Preferences.cpp:71 +msgid "" +"If enabled, PrusaSlicer will check for the new versions of itself online. " +"When a new version becomes available a notification is displayed at the next " +"application startup (never during program usage). This is only a " +"notification mechanisms, no automatic installation is done." +msgstr "" +"Se habilitado, PrusaSlicer irá verificar as novas versões de si mesmo on-" +"line. Quando uma nova versão se torna disponível, uma notificação é exibida " +"na próxima inicialização do aplicativo (nunca durante o uso do programa). " +"Este é apenas um mecanismos de notificação, nenhuma instalação automática é " +"feita." + +#: src/slic3r/GUI/Preferences.cpp:79 +msgid "" +"If enabled, Slic3r downloads updates of built-in system presets in the " +"background. These updates are downloaded into a separate temporary location. " +"When a new preset version becomes available it is offered at application " +"startup." +msgstr "" +"Se ativada, o Slic3r baixa atualizações de predefinições de sistema " +"incorporadas em segundo plano. Essas atualizações são baixadas em um local " +"temporário separado. Quando uma nova versão predefinida se torna disponível, " +"ela é oferecida na inicialização do aplicativo." + +#: src/slic3r/GUI/Preferences.cpp:84 +msgid "Suppress \" - default - \" presets" +msgstr "Suprimir predefinições \"-padrão-\"" + +#: src/slic3r/GUI/Preferences.cpp:86 +msgid "" +"Suppress \" - default - \" presets in the Print / Filament / Printer " +"selections once there are any other valid presets available." +msgstr "" +"Suprimir predefinições \"-padrão-\" em impressão/filamento/impressora, uma " +"vez que existam outras predefinições válidas disponíveis." + +#: src/slic3r/GUI/Preferences.cpp:92 +msgid "Show incompatible print and filament presets" +msgstr "Mostrar predefinições de impressão e filamento incompatíveis" + +#: src/slic3r/GUI/Preferences.cpp:94 +msgid "" +"When checked, the print and filament presets are shown in the preset editor " +"even if they are marked as incompatible with the active printer" +msgstr "" +"Quando marcada, as predefinições de impressão e filamento são mostradas no " +"editor de predefinições, mesmo que estejam marcadas como incompatíveis com a " +"impressora ativa" + +#: src/slic3r/GUI/Preferences.cpp:101 +msgid "Use Retina resolution for the 3D scene" +msgstr "Usar a resolução retina para a cena 3D" + +#: src/slic3r/GUI/Preferences.cpp:103 +msgid "" +"If enabled, the 3D scene will be rendered in Retina resolution. If you are " +"experiencing 3D performance problems, disabling this option may help." +msgstr "" +"Se ativada, a cena 3D será renderizada na resolução retina. Se você estiver " +"enfrentando problemas de desempenho 3D, desabilitar essa opção pode ajudar." + +#: src/slic3r/GUI/Preferences.cpp:110 +msgid "Use perspective camera" +msgstr "Usar a câmera em perspectiva" + +#: src/slic3r/GUI/Preferences.cpp:112 +msgid "" +"If enabled, use perspective camera. If not enabled, use orthographic camera." +msgstr "" +"Se ativada, use a câmera em perspectiva. Se não estiver ativada, use a " +"câmera ortográfica." + +#: src/slic3r/GUI/Preferences.cpp:117 +msgid "Use custom size for toolbar icons" +msgstr "Usar tamanho personalizado para ícones da barra de ferramentas" + +#: src/slic3r/GUI/Preferences.cpp:119 +msgid "If enabled, you can change size of toolbar icons manually." +msgstr "" +"Se ativado, você pode alterar o tamanho dos ícones da barra de ferramentas " +"manualmente." + +#: src/slic3r/GUI/Preferences.cpp:144 +#, c-format +msgid "You need to restart %s to make the changes effective." +msgstr "Você precisa reiniciar %s para tornar as alterações efetivas." + +#: src/slic3r/GUI/Preferences.cpp:192 +msgid "Icon size in a respect to the default size" +msgstr "Tamanho do ícone em relação ao tamanho padrão" + +#: src/slic3r/GUI/Preferences.cpp:207 +msgid "Select toolbar icon size in respect to the default one." +msgstr "" +"Selecione o tamanho do ícone da barra de ferramentas em relação ao padrão." + +#: src/slic3r/GUI/Preset.cpp:212 +msgid "modified" +msgstr "modificado" + +#: src/slic3r/GUI/Preset.cpp:967 src/slic3r/GUI/Preset.cpp:1007 +#: src/slic3r/GUI/Preset.cpp:1072 src/slic3r/GUI/Preset.cpp:1104 +#: src/slic3r/GUI/PresetBundle.cpp:1484 src/slic3r/GUI/PresetBundle.cpp:1559 +msgid "System presets" +msgstr "Predefinições do sistema" + +#: src/slic3r/GUI/Preset.cpp:1011 src/slic3r/GUI/Preset.cpp:1108 +#: src/slic3r/GUI/PresetBundle.cpp:1564 +msgid "User presets" +msgstr "Predefinições do usuário" + +#: src/slic3r/GUI/Preset.cpp:1040 src/slic3r/GUI/Tab.cpp:243 +msgid "Add a new printer" +msgstr "Adicionar uma nova impressora" + +#: src/slic3r/GUI/Preset.cpp:1312 +msgid "filament" +msgstr "filamento" + +#: src/slic3r/GUI/Preset.cpp:1313 +msgid "SLA print" +msgstr "Impressão de SLA" + +#: src/slic3r/GUI/PresetHints.cpp:28 +msgid "" +"If estimated layer time is below ~%1%s, fan will run at %2%%% and print " +"speed will be reduced so that no less than %3%s are spent on that layer " +"(however, speed will never be reduced below %4%mm/s)." +msgstr "" +"Se o tempo estimado da camada estiver abaixo de ~%1%s, o ventoinha será " +"executado em %2%%% e a velocidade de impressão será reduzida para que não " +"menos de %3%s sejam gastos nessa camada (no entanto, a velocidade nunca será " +"reduzida abaixo de %4% mm/s)." + +#: src/slic3r/GUI/PresetHints.cpp:35 +msgid "" +"\n" +"If estimated layer time is greater, but still below ~%1%s, fan will run at a " +"proportionally decreasing speed between %2%%% and %3%%%." +msgstr "" +"\n" +"Se o tempo estimado da camada for maior, mas ainda abaixo de ~%1%s, o " +"ventoinha será executado em uma velocidade proporcionalmente decrescente " +"entre %2%%% e %3%%%." + +#: src/slic3r/GUI/PresetHints.cpp:39 +msgid "" +"\n" +"During the other layers, fan" +msgstr "" +"\n" +"Durante as outras camadas, o ventoinha" + +#: src/slic3r/GUI/PresetHints.cpp:41 +msgid "Fan" +msgstr "Ventoinha" + +#: src/slic3r/GUI/PresetHints.cpp:47 +msgid "will always run at %1%%%" +msgstr "será sempre executado em %1%%%" + +#: src/slic3r/GUI/PresetHints.cpp:50 +msgid "except for the first %1% layers." +msgstr "exceto para as primeiras camadas %1%." + +#: src/slic3r/GUI/PresetHints.cpp:52 +msgid "except for the first layer." +msgstr "exceto para a primeira camada." + +#: src/slic3r/GUI/PresetHints.cpp:54 +msgid "will be turned off." +msgstr "será desligado." + +#: src/slic3r/GUI/PresetHints.cpp:155 +msgid "external perimeters" +msgstr "perímetros externos" + +#: src/slic3r/GUI/PresetHints.cpp:164 +msgid "perimeters" +msgstr "perímetros" + +#: src/slic3r/GUI/PresetHints.cpp:173 +msgid "infill" +msgstr "preenchimento" + +#: src/slic3r/GUI/PresetHints.cpp:183 +msgid "solid infill" +msgstr "preenchimento sólido" + +#: src/slic3r/GUI/PresetHints.cpp:191 +msgid "top solid infill" +msgstr "preenchimento sólido do topo" + +#: src/slic3r/GUI/PresetHints.cpp:202 +msgid "support" +msgstr "suporte" + +#: src/slic3r/GUI/PresetHints.cpp:212 +msgid "support interface" +msgstr "interface de suporte" + +#: src/slic3r/GUI/PresetHints.cpp:218 +msgid "First layer volumetric" +msgstr "Primeira camada volumétrica" + +#: src/slic3r/GUI/PresetHints.cpp:218 +msgid "Bridging volumetric" +msgstr "Ponteamento volumétrico" + +#: src/slic3r/GUI/PresetHints.cpp:218 +msgid "Volumetric" +msgstr "Volumétrica" + +#: src/slic3r/GUI/PresetHints.cpp:219 +msgid "flow rate is maximized" +msgstr "a taxa de fluxo é maximizada" + +#: src/slic3r/GUI/PresetHints.cpp:222 +msgid "by the print profile maximum" +msgstr "pelo perfil de impressão máximo" + +#: src/slic3r/GUI/PresetHints.cpp:223 +msgid "when printing" +msgstr "ao imprimir" + +#: src/slic3r/GUI/PresetHints.cpp:224 +msgid "with a volumetric rate" +msgstr "com uma taxa volumétrica" + +#: src/slic3r/GUI/PresetHints.cpp:228 +#, c-format +msgid "%3.2f mm³/s at filament speed %3.2f mm/s." +msgstr "%3.2f mm ³/s na velocidade do filamento %3.2f mm/s." + +#: src/slic3r/GUI/PresetHints.cpp:246 +msgid "" +"Recommended object thin wall thickness: Not available due to invalid layer " +"height." +msgstr "" +"Espessura de parede fina do objeto recomendado: não disponível devido à " +"altura da camada inválida." + +#: src/slic3r/GUI/PresetHints.cpp:262 +#, c-format +msgid "Recommended object thin wall thickness for layer height %.2f and" +msgstr "" +"Espessura de parede fina do objeto recomendado para a altura da camada %.2f e" + +#: src/slic3r/GUI/PresetHints.cpp:268 +#, c-format +msgid "%d lines: %.2f mm" +msgstr "%d linhas: %.2f mm" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:33 +msgid "Send G-Code to printer host" +msgstr "Enviar G-code para o host da impressora" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:33 +msgid "Upload to Printer Host with the following filename:" +msgstr "Carregue para o host da impressora com o seguinte nome de arquivo:" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:35 +msgid "Start printing after upload" +msgstr "Iniciar a impressão após o envio" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:42 +msgid "Use forward slashes ( / ) as a directory separator if needed." +msgstr "Use barras (/) como um separador de diretório, se necessário." + +#: src/slic3r/GUI/PrintHostDialogs.cpp:149 +msgid "ID" +msgstr "ID" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:150 +msgid "Progress" +msgstr "Progresso" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:151 +msgid "Status" +msgstr "Status" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:152 +msgid "Host" +msgstr "Servidor" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:153 +msgid "Filename" +msgstr "Nome do arquivo" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:154 +msgid "Error Message" +msgstr "Mensagem de Erro" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:157 +msgid "Cancel selected" +msgstr "Cancelar selecionado" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:159 +msgid "Show error message" +msgstr "Exibir mensagem de erro" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:198 +#: src/slic3r/GUI/PrintHostDialogs.cpp:229 +msgid "Enqueued" +msgstr "Enfileirado" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:230 +msgid "Uploading" +msgstr "Enviando" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:234 +msgid "Completed" +msgstr "Concluído" + +#: src/slic3r/GUI/PrintHostDialogs.cpp:272 +msgid "Error uploading to print host:" +msgstr "Erro ao carregar para o host de impressão:" + +#: src/slic3r/GUI/RammingChart.cpp:23 +msgid "NO RAMMING AT ALL" +msgstr "Não usar Ramming" + +#: src/slic3r/GUI/RammingChart.cpp:76 +msgid "Time" +msgstr "Tempo" + +#: src/slic3r/GUI/RammingChart.cpp:76 src/slic3r/GUI/WipeTowerDialog.cpp:82 +#: src/libslic3r/PrintConfig.cpp:627 src/libslic3r/PrintConfig.cpp:671 +#: src/libslic3r/PrintConfig.cpp:686 src/libslic3r/PrintConfig.cpp:2349 +#: src/libslic3r/PrintConfig.cpp:2358 src/libslic3r/PrintConfig.cpp:2418 +#: src/libslic3r/PrintConfig.cpp:2426 src/libslic3r/PrintConfig.cpp:2434 +#: src/libslic3r/PrintConfig.cpp:2441 src/libslic3r/PrintConfig.cpp:2449 +#: src/libslic3r/PrintConfig.cpp:2457 +msgid "s" +msgstr "s" + +#: src/slic3r/GUI/RammingChart.cpp:81 +msgid "Volumetric speed" +msgstr "Velocidade volumétrica" + +#: src/slic3r/GUI/RammingChart.cpp:81 src/libslic3r/PrintConfig.cpp:584 +#: src/libslic3r/PrintConfig.cpp:1234 +msgid "mm³/s" +msgstr "mm ³/s" + +#: src/slic3r/GUI/Selection.cpp:146 +msgid "Selection-Add" +msgstr "Seleção-Adicionar" + +#: src/slic3r/GUI/Selection.cpp:187 +msgid "Selection-Remove" +msgstr "Seleção-remover" + +#: src/slic3r/GUI/Selection.cpp:219 +msgid "Selection-Add Object" +msgstr "Seleção-Adicionar objeto" + +#: src/slic3r/GUI/Selection.cpp:238 +msgid "Selection-Remove Object" +msgstr "Seleção-remover objeto" + +#: src/slic3r/GUI/Selection.cpp:256 +msgid "Selection-Add Instance" +msgstr "Instância de seleção-Adicionar" + +#: src/slic3r/GUI/Selection.cpp:275 +msgid "Selection-Remove Instance" +msgstr "Seleção-remover instância" + +#: src/slic3r/GUI/Selection.cpp:376 +msgid "Selection-Add All" +msgstr "Seleção-adicionar todos" + +#: src/slic3r/GUI/Selection.cpp:402 +msgid "Selection-Remove All" +msgstr "Seleção-remover todos" + +#: src/slic3r/GUI/Selection.cpp:939 +msgid "Scale To Fit" +msgstr "Dimensionar para caber" + +#: src/slic3r/GUI/Selection.cpp:1474 +msgid "Set Printable Instance" +msgstr "Definir instância imprimível" + +#: src/slic3r/GUI/Selection.cpp:1474 +msgid "Set Unprintable Instance" +msgstr "Definir instância não imprimível" + +#: src/slic3r/GUI/SysInfoDialog.cpp:78 +msgid "System Information" +msgstr "Informações do sistema" + +#: src/slic3r/GUI/SysInfoDialog.cpp:154 +msgid "Copy to Clipboard" +msgstr "Copiar para a Área de Transferência" + +#: src/slic3r/GUI/Tab.cpp:52 src/libslic3r/PrintConfig.cpp:239 +msgid "Compatible printers" +msgstr "Impressoras compatíveis" + +#: src/slic3r/GUI/Tab.cpp:53 +msgid "Select the printers this profile is compatible with." +msgstr "Selecione as impressoras com as quais este perfil é compatível." + +#: src/slic3r/GUI/Tab.cpp:58 src/libslic3r/PrintConfig.cpp:254 +msgid "Compatible print profiles" +msgstr "Perfis de impressão compatíveis" + +#: src/slic3r/GUI/Tab.cpp:59 +msgid "Select the print profiles this profile is compatible with." +msgstr "" +"Selecione os perfis de impressão com os quais este perfil é compatível." + +#. TRN "Save current Settings" +#: src/slic3r/GUI/Tab.cpp:135 +#, c-format +msgid "Save current %s" +msgstr "Salvar %s atual" + +#: src/slic3r/GUI/Tab.cpp:136 +msgid "Delete this preset" +msgstr "Exclua esta predefinição" + +#: src/slic3r/GUI/Tab.cpp:141 +msgid "" +"Hover the cursor over buttons to find more information \n" +"or click this button." +msgstr "" +"Passe o cursor sobre os botões para encontrar mais informações \n" +"ou clique neste botão." + +#: src/slic3r/GUI/Tab.cpp:943 +msgid "This is a default preset." +msgstr "Esta é uma predefinição padrão." + +#: src/slic3r/GUI/Tab.cpp:945 +msgid "This is a system preset." +msgstr "Esta é uma predefinição do sistema." + +#: src/slic3r/GUI/Tab.cpp:947 +msgid "Current preset is inherited from the default preset." +msgstr "Predefinição atual é herdada da predefinição padrão." + +#: src/slic3r/GUI/Tab.cpp:950 +#, c-format +msgid "" +"Current preset is inherited from:\n" +"\t%s" +msgstr "" +"Predefinição atual é herdada de:\n" +"\t%s" + +#: src/slic3r/GUI/Tab.cpp:954 +msgid "It can't be deleted or modified." +msgstr "Ele não pode ser excluído ou modificado." + +#: src/slic3r/GUI/Tab.cpp:955 +msgid "" +"Any modifications should be saved as a new preset inherited from this one." +msgstr "" +"Todas as modificações devem ser salvas como uma nova predefinição herdada de " +"uma presente." + +#: src/slic3r/GUI/Tab.cpp:956 +msgid "To do that please specify a new name for the preset." +msgstr "Para fazer isso, especifique um novo nome para a predefinição." + +#: src/slic3r/GUI/Tab.cpp:960 +msgid "Additional information:" +msgstr "Informações adicionais:" + +#: src/slic3r/GUI/Tab.cpp:966 +msgid "printer model" +msgstr "modelo de impressora" + +#: src/slic3r/GUI/Tab.cpp:974 +msgid "default print profile" +msgstr "perfil de impressão padrão" + +#: src/slic3r/GUI/Tab.cpp:977 +msgid "default filament profile" +msgstr "perfil de filamento padrão" + +#: src/slic3r/GUI/Tab.cpp:991 +msgid "default SLA material profile" +msgstr "perfil de material SLA padrão" + +#: src/slic3r/GUI/Tab.cpp:995 +msgid "default SLA print profile" +msgstr "perfil de impressão padrão do SLA" + +#: src/slic3r/GUI/Tab.cpp:1032 src/slic3r/GUI/Tab.cpp:3731 +msgid "Layers and perimeters" +msgstr "Camadas e perímetros" + +#: src/slic3r/GUI/Tab.cpp:1037 +msgid "Vertical shells" +msgstr "Paredes verticais" + +#: src/slic3r/GUI/Tab.cpp:1048 +msgid "Horizontal shells" +msgstr "Paredes horizontais" + +#: src/slic3r/GUI/Tab.cpp:1049 src/libslic3r/PrintConfig.cpp:1759 +msgid "Solid layers" +msgstr "Camadas sólidas" + +#: src/slic3r/GUI/Tab.cpp:1054 +msgid "Quality (slower slicing)" +msgstr "Qualidade (fatiamento mais lento)" + +#: src/slic3r/GUI/Tab.cpp:1072 +msgid "Reducing printing time" +msgstr "Reduzindo o tempo de impressão" + +#: src/slic3r/GUI/Tab.cpp:1084 +msgid "Skirt and brim" +msgstr "Saia e aba" + +#: src/slic3r/GUI/Tab.cpp:1101 +msgid "Raft" +msgstr "Estrado" + +#: src/slic3r/GUI/Tab.cpp:1105 +msgid "Options for support material and raft" +msgstr "Opções para material de suporte e estrado" + +#: src/slic3r/GUI/Tab.cpp:1120 +msgid "Speed for print moves" +msgstr "Velocidade para movimentos de impressão" + +#: src/slic3r/GUI/Tab.cpp:1132 +msgid "Speed for non-print moves" +msgstr "Velocidade para movimentos não impressos" + +#: src/slic3r/GUI/Tab.cpp:1135 +msgid "Modifiers" +msgstr "Modificadores" + +#: src/slic3r/GUI/Tab.cpp:1138 +msgid "Acceleration control (advanced)" +msgstr "Controle de aceleração (avançado)" + +#: src/slic3r/GUI/Tab.cpp:1145 +msgid "Autospeed (advanced)" +msgstr "Velocidade automática (avançado)" + +#: src/slic3r/GUI/Tab.cpp:1153 +msgid "Multiple Extruders" +msgstr "Extrusoras múltiplas" + +#: src/slic3r/GUI/Tab.cpp:1161 +msgid "Ooze prevention" +msgstr "Prevenção de vazão" + +#: src/slic3r/GUI/Tab.cpp:1178 +msgid "Extrusion width" +msgstr "Espessura da extrusão" + +#: src/slic3r/GUI/Tab.cpp:1188 +msgid "Overlap" +msgstr "Cobrir" + +#: src/slic3r/GUI/Tab.cpp:1191 +msgid "Flow" +msgstr "Fluxo" + +#: src/slic3r/GUI/Tab.cpp:1200 +msgid "Other" +msgstr "Outro" + +#: src/slic3r/GUI/Tab.cpp:1203 src/slic3r/GUI/Tab.cpp:3789 +msgid "Output options" +msgstr "Opções de saída" + +#: src/slic3r/GUI/Tab.cpp:1204 +msgid "Sequential printing" +msgstr "Impressão sequencial" + +#: src/slic3r/GUI/Tab.cpp:1206 +msgid "Extruder clearance (mm)" +msgstr "Folga da extrusora (milímetro)" + +#: src/slic3r/GUI/Tab.cpp:1215 src/slic3r/GUI/Tab.cpp:3790 +msgid "Output file" +msgstr "Arquivo de saída" + +#: src/slic3r/GUI/Tab.cpp:1222 src/libslic3r/PrintConfig.cpp:1432 +msgid "Post-processing scripts" +msgstr "Scripts de pós-processamento" + +#: src/slic3r/GUI/Tab.cpp:1228 src/slic3r/GUI/Tab.cpp:1229 +#: src/slic3r/GUI/Tab.cpp:1752 src/slic3r/GUI/Tab.cpp:1753 +#: src/slic3r/GUI/Tab.cpp:2214 src/slic3r/GUI/Tab.cpp:2215 +#: src/slic3r/GUI/Tab.cpp:2328 src/slic3r/GUI/Tab.cpp:2329 +#: src/slic3r/GUI/Tab.cpp:3668 src/slic3r/GUI/Tab.cpp:3669 +msgid "Notes" +msgstr "Notas" + +#: src/slic3r/GUI/Tab.cpp:1235 src/slic3r/GUI/Tab.cpp:1760 +#: src/slic3r/GUI/Tab.cpp:2221 src/slic3r/GUI/Tab.cpp:2335 +#: src/slic3r/GUI/Tab.cpp:3676 src/slic3r/GUI/Tab.cpp:3795 +msgid "Dependencies" +msgstr "Dependências" + +#: src/slic3r/GUI/Tab.cpp:1236 src/slic3r/GUI/Tab.cpp:1761 +#: src/slic3r/GUI/Tab.cpp:2222 src/slic3r/GUI/Tab.cpp:2336 +#: src/slic3r/GUI/Tab.cpp:3677 src/slic3r/GUI/Tab.cpp:3796 +msgid "Profile dependencies" +msgstr "Dependências de perfil" + +#: src/slic3r/GUI/Tab.cpp:1538 src/slic3r/GUI/Tab.cpp:1593 +msgid "Filament Overrides" +msgstr "Sobrescrever config." + +#: src/slic3r/GUI/Tab.cpp:1539 src/slic3r/GUI/Tab.cpp:1598 +#: src/slic3r/GUI/Tab.cpp:2570 +msgid "Retraction" +msgstr "Retração" + +#: src/slic3r/GUI/Tab.cpp:1648 src/libslic3r/PrintConfig.cpp:2030 +msgid "Temperature" +msgstr "Temperatura" + +#: src/slic3r/GUI/Tab.cpp:1654 +msgid "Bed" +msgstr "Mesa" + +#: src/slic3r/GUI/Tab.cpp:1659 +msgid "Cooling" +msgstr "Resfriamento" + +#: src/slic3r/GUI/Tab.cpp:1660 src/libslic3r/PrintConfig.cpp:1335 +#: src/libslic3r/PrintConfig.cpp:2150 +msgid "Enable" +msgstr "Habilitar" + +#: src/slic3r/GUI/Tab.cpp:1671 +msgid "Fan settings" +msgstr "Config. da ventoinha" + +#: src/slic3r/GUI/Tab.cpp:1672 +msgid "Fan speed" +msgstr "Velocidade do ventoinha" + +#: src/slic3r/GUI/Tab.cpp:1680 +msgid "Cooling thresholds" +msgstr "Limiares de resfriamento" + +#: src/slic3r/GUI/Tab.cpp:1686 +msgid "Filament properties" +msgstr "Propriedades de filamento" + +#: src/slic3r/GUI/Tab.cpp:1690 +msgid "Print speed override" +msgstr "Substituição da velocidade de impressão" + +#: src/slic3r/GUI/Tab.cpp:1700 +msgid "Wipe tower parameters" +msgstr "Parâmetros da torre de limpeza" + +#: src/slic3r/GUI/Tab.cpp:1703 +msgid "Toolchange parameters with single extruder MM printers" +msgstr "" +"Parâmetros de mudança de ferramenta com impressoras de multi material com " +"apenas uma extrusora" + +#: src/slic3r/GUI/Tab.cpp:1717 +msgid "Ramming settings" +msgstr "config. de Ramming" + +#: src/slic3r/GUI/Tab.cpp:1739 src/slic3r/GUI/Tab.cpp:2177 +msgid "Custom G-code" +msgstr "G-code customizado" + +#: src/slic3r/GUI/Tab.cpp:1740 src/slic3r/GUI/Tab.cpp:2178 +#: src/libslic3r/PrintConfig.cpp:1785 src/libslic3r/PrintConfig.cpp:1800 +msgid "Start G-code" +msgstr "G-code de início" + +#: src/slic3r/GUI/Tab.cpp:1746 src/slic3r/GUI/Tab.cpp:2184 +#: src/libslic3r/PrintConfig.cpp:369 src/libslic3r/PrintConfig.cpp:379 +msgid "End G-code" +msgstr "G-code de finalização" + +#: src/slic3r/GUI/Tab.cpp:1803 +msgid "Volumetric flow hints not available" +msgstr "Dicas de fluxo volumétrico não disponíveis" + +#: src/slic3r/GUI/Tab.cpp:1889 src/slic3r/GUI/Tab.cpp:2117 +msgid "Test" +msgstr "Teste" + +#: src/slic3r/GUI/Tab.cpp:1899 +msgid "Could not get a valid Printer Host reference" +msgstr "Não foi possível obter uma referência de host de impressora válida" + +#: src/slic3r/GUI/Tab.cpp:1905 src/slic3r/GUI/Tab.cpp:2130 +msgid "Success!" +msgstr "Sucesso!" + +#: src/slic3r/GUI/Tab.cpp:1920 +msgid "" +"HTTPS CA file is optional. It is only needed if you use HTTPS with a self-" +"signed certificate." +msgstr "" +"O arquivo HTTPS CA é opcional. Só é necessário se você usar HTTPS com um " +"certificado auto-assinado." + +#: src/slic3r/GUI/Tab.cpp:1933 +msgid "Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*" +msgstr "" +"Arquivos de certificado (*. CRT, *. pem) | *. CRT; *. pem | Todos os " +"arquivos | *. *" + +#: src/slic3r/GUI/Tab.cpp:1934 +msgid "Open CA certificate file" +msgstr "Abra o arquivo de certificado da CA" + +#: src/slic3r/GUI/Tab.cpp:1962 +#, c-format +msgid "" +"HTTPS CA File:\n" +" \tOn this system, %s uses HTTPS certificates from the system Certificate " +"Store or Keychain.\n" +" \tTo use a custom CA file, please import your CA file into Certificate " +"Store / Keychain." +msgstr "" +"Arquivo HTTPS CA:\n" +" \tNeste sistema, %s usa certificados HTTPS do sistema Certificate Store " +"ou keychain.\n" +" \tPara usar um arquivo de CA personalizado, importe seu arquivo de CA " +"para o repositório de certificados/chaveiro." + +#: src/slic3r/GUI/Tab.cpp:2002 src/slic3r/GUI/Tab.cpp:2243 +msgid "Size and coordinates" +msgstr "Tamanho e coordenadas" + +#: src/slic3r/GUI/Tab.cpp:2007 src/slic3r/GUI/Tab.cpp:2248 +#: src/slic3r/GUI/Tab.cpp:3338 +msgid "Set" +msgstr "Definir" + +#: src/slic3r/GUI/Tab.cpp:2039 +msgid "Capabilities" +msgstr "Capacidades" + +#: src/slic3r/GUI/Tab.cpp:2044 +msgid "Number of extruders of the printer." +msgstr "Número de extrusoras da impressora." + +#: src/slic3r/GUI/Tab.cpp:2069 +msgid "" +"Single Extruder Multi Material is selected, \n" +"and all extruders must have the same diameter.\n" +"Do you want to change the diameter for all extruders to first extruder " +"nozzle diameter value?" +msgstr "" +"A extrusora multi material é selecionada, \n" +"e todas as extrusoras devem ter o mesmo diâmetro.\n" +"Você quer mudar o diâmetro para todas as extrusoras ao primeiro valor do " +"diâmetro da ponteira da extrusora?" + +#: src/slic3r/GUI/Tab.cpp:2072 src/slic3r/GUI/Tab.cpp:2540 +#: src/libslic3r/PrintConfig.cpp:1310 +msgid "Nozzle diameter" +msgstr "Diâmetro do bico" + +#: src/slic3r/GUI/Tab.cpp:2102 +msgid "USB/Serial connection" +msgstr "Conexão USB/serial" + +#: src/slic3r/GUI/Tab.cpp:2103 src/libslic3r/PrintConfig.cpp:1640 +msgid "Serial port" +msgstr "Porte Serial" + +#: src/slic3r/GUI/Tab.cpp:2108 +msgid "Rescan serial ports" +msgstr "Portas seriais de Rescan" + +#: src/slic3r/GUI/Tab.cpp:2130 +msgid "Connection to printer works correctly." +msgstr "A ligação à impressora funciona corretamente." + +#: src/slic3r/GUI/Tab.cpp:2133 +msgid "Connection failed." +msgstr "A conexão falhou." + +#: src/slic3r/GUI/Tab.cpp:2146 src/slic3r/GUI/Tab.cpp:2323 +msgid "Print Host upload" +msgstr "Upload do host de impressão" + +#: src/slic3r/GUI/Tab.cpp:2190 src/libslic3r/PrintConfig.cpp:138 +msgid "Before layer change G-code" +msgstr "Antes da mudança de camada G-code" + +#: src/slic3r/GUI/Tab.cpp:2196 src/libslic3r/PrintConfig.cpp:1056 +msgid "After layer change G-code" +msgstr "Após a mudança da camada do G-code" + +#: src/slic3r/GUI/Tab.cpp:2202 src/libslic3r/PrintConfig.cpp:2056 +msgid "Tool change G-code" +msgstr "G-code de troca de ferramenta" + +#: src/slic3r/GUI/Tab.cpp:2208 +msgid "Between objects G-code (for sequential printing)" +msgstr "G-code entre objetos (para impressão sequencial)" + +#: src/slic3r/GUI/Tab.cpp:2280 +msgid "Display" +msgstr "Exibição" + +#: src/slic3r/GUI/Tab.cpp:2295 +msgid "Tilt" +msgstr "Inclinar" + +#: src/slic3r/GUI/Tab.cpp:2296 +msgid "Tilt time" +msgstr "Tempo de inclinação" + +#: src/slic3r/GUI/Tab.cpp:2302 src/slic3r/GUI/Tab.cpp:3650 +msgid "Corrections" +msgstr "Correções" + +#: src/slic3r/GUI/Tab.cpp:2317 src/slic3r/GUI/Tab.cpp:3646 +msgid "Exposure" +msgstr "Exposição" + +#: src/slic3r/GUI/Tab.cpp:2388 src/slic3r/GUI/Tab.cpp:2473 +#: src/libslic3r/PrintConfig.cpp:1106 src/libslic3r/PrintConfig.cpp:1124 +#: src/libslic3r/PrintConfig.cpp:1142 src/libslic3r/PrintConfig.cpp:1159 +#: src/libslic3r/PrintConfig.cpp:1170 src/libslic3r/PrintConfig.cpp:1181 +#: src/libslic3r/PrintConfig.cpp:1192 +msgid "Machine limits" +msgstr "Limites da máquina" + +#: src/slic3r/GUI/Tab.cpp:2402 +msgid "Values in this column are for Normal mode" +msgstr "Valores nesta coluna são para o modo normal" + +#: src/slic3r/GUI/Tab.cpp:2403 +msgid "Normal" +msgstr "Normal" + +#: src/slic3r/GUI/Tab.cpp:2408 +msgid "Values in this column are for Stealth mode" +msgstr "Valores nesta coluna são para o modo furtivo" + +#: src/slic3r/GUI/Tab.cpp:2409 +msgid "Stealth" +msgstr "Furtivo" + +#: src/slic3r/GUI/Tab.cpp:2417 +msgid "Maximum feedrates" +msgstr "Velocidade máxima de alimentação" + +#: src/slic3r/GUI/Tab.cpp:2422 +msgid "Maximum accelerations" +msgstr "Acelerações máximas" + +#: src/slic3r/GUI/Tab.cpp:2429 +msgid "Jerk limits" +msgstr "Limites de empurrão" + +#: src/slic3r/GUI/Tab.cpp:2434 +msgid "Minimum feedrates" +msgstr "Velocidades alimentação mínimos" + +#: src/slic3r/GUI/Tab.cpp:2498 src/slic3r/GUI/Tab.cpp:2506 +msgid "Single extruder MM setup" +msgstr "config. de extrusora multi material" + +#: src/slic3r/GUI/Tab.cpp:2507 +msgid "Single extruder multimaterial parameters" +msgstr "Parâmetros para extrusora única multimaterial" + +#: src/slic3r/GUI/Tab.cpp:2520 src/libslic3r/GCode/PreviewData.cpp:461 +#, c-format +msgid "Extruder %d" +msgstr "Extrusora %d" + +#: src/slic3r/GUI/Tab.cpp:2538 +msgid "" +"This is a single extruder multimaterial printer, diameters of all extruders " +"will be set to the new value. Do you want to proceed?" +msgstr "" +"Esta é uma única impressora multimaterial extrusora, diâmetros de todas as " +"extrusoras será definido para o novo valor. Você quer prosseguir?" + +#: src/slic3r/GUI/Tab.cpp:2562 +msgid "Layer height limits" +msgstr "Limites de altura da camada" + +#: src/slic3r/GUI/Tab.cpp:2567 +msgid "Position (for multi-extruder printers)" +msgstr "Posição (para impressoras multiextrusoras)" + +#: src/slic3r/GUI/Tab.cpp:2573 +msgid "Only lift Z" +msgstr "Apenas elevar Z" + +#: src/slic3r/GUI/Tab.cpp:2586 +msgid "" +"Retraction when tool is disabled (advanced settings for multi-extruder " +"setups)" +msgstr "" +"Retração quando a ferramenta está desativada (config. avançadas para " +"instalações multiextrusoras)" + +#: src/slic3r/GUI/Tab.cpp:2594 +msgid "Reset to Filament Color" +msgstr "Restabelecer cor do filamento" + +#: src/slic3r/GUI/Tab.cpp:2775 +msgid "" +"The Wipe option is not available when using the Firmware Retraction mode.\n" +"\n" +"Shall I disable it in order to enable Firmware Retraction?" +msgstr "" +"A opção limpar não está disponível ao usar o modo de retração de firmware.\n" +"\n" +"Devo desativá-lo, a fim de permitir a retração de firmware?" + +#: src/slic3r/GUI/Tab.cpp:2777 +msgid "Firmware Retraction" +msgstr "Retração do firmware" + +#: src/slic3r/GUI/Tab.cpp:3106 +#, c-format +msgid "Default preset (%s)" +msgstr "Predefinição padrão ( %s)" + +#: src/slic3r/GUI/Tab.cpp:3107 +#, c-format +msgid "Preset (%s)" +msgstr "Predefinição ( %s)" + +#: src/slic3r/GUI/Tab.cpp:3124 +msgid "has the following unsaved changes:" +msgstr "tem as seguintes alterações não salvas:" + +#: src/slic3r/GUI/Tab.cpp:3127 +msgid "is not compatible with printer" +msgstr "não é compatível com a impressora" + +#: src/slic3r/GUI/Tab.cpp:3128 +msgid "is not compatible with print profile" +msgstr "não é compatível com o perfil de impressão" + +#: src/slic3r/GUI/Tab.cpp:3130 +msgid "and it has the following unsaved changes:" +msgstr "e tem as seguintes alterações não salvas:" + +#: src/slic3r/GUI/Tab.cpp:3134 +msgid "Unsaved Changes" +msgstr "Alterações não salvas" + +#: src/slic3r/GUI/Tab.cpp:3225 +msgid "%1% - Copy" +msgstr "%1% - cópia" + +#: src/slic3r/GUI/Tab.cpp:3248 +msgid "The supplied name is empty. It can't be saved." +msgstr "O nome fornecido está vazio. Não pode ser salvo." + +#: src/slic3r/GUI/Tab.cpp:3253 +msgid "Cannot overwrite a system profile." +msgstr "Não é possível substituir um perfil de sistema." + +#: src/slic3r/GUI/Tab.cpp:3257 +msgid "Cannot overwrite an external profile." +msgstr "Não é possível substituir um perfil externo." + +#: src/slic3r/GUI/Tab.cpp:3283 +msgid "remove" +msgstr "remover" + +#: src/slic3r/GUI/Tab.cpp:3283 +msgid "delete" +msgstr "excluir" + +#. TRN remove/delete +#: src/slic3r/GUI/Tab.cpp:3285 +msgid "Are you sure you want to %1% the selected preset?" +msgstr "Tem certeza de que deseja %1% da predefinição selecionada?" + +#. TRN Remove/Delete +#: src/slic3r/GUI/Tab.cpp:3288 +msgid "%1% Preset" +msgstr "%1% Predefinição" + +#: src/slic3r/GUI/Tab.cpp:3414 +msgid "LOCKED LOCK" +msgstr "CADEADO FECHADO" + +#. TRN Description for "LOCKED LOCK" +#: src/slic3r/GUI/Tab.cpp:3416 +msgid "" +"indicates that the settings are the same as the system (or default) values " +"for the current option group" +msgstr "" +"indica que as config. são as mesmas que os valores do sistema (ou padrão) " +"para o grupo de opções atual" + +#: src/slic3r/GUI/Tab.cpp:3418 +msgid "UNLOCKED LOCK" +msgstr "CADEADO ABERTO" + +#. TRN Description for "UNLOCKED LOCK" +#: src/slic3r/GUI/Tab.cpp:3420 +msgid "" +"indicates that some settings were changed and are not equal to the system " +"(or default) values for the current option group.\n" +"Click the UNLOCKED LOCK icon to reset all settings for current option group " +"to the system (or default) values." +msgstr "" +"indica que algumas config. foram alteradas e não são iguais aos valores do " +"sistema (ou padrão) para o grupo de opções atual.\n" +"Clique no ícone DESBLOQUEAR para redefinir todas as config. do grupo de " +"opções atual para os valores do sistema (ou padrão)." + +#: src/slic3r/GUI/Tab.cpp:3425 +msgid "WHITE BULLET" +msgstr "PONTO BRANCO" + +#. TRN Description for "WHITE BULLET" +#: src/slic3r/GUI/Tab.cpp:3427 +msgid "" +"for the left button: \tindicates a non-system (or non-default) preset,\n" +"for the right button: \tindicates that the settings hasn't been modified." +msgstr "" +"para o botão esquerdo: \t indica uma predefinição que não é do sistema (ou " +"não-padrão),\n" +"para o botão direito: \t indica que as config. não foram modificadas." + +#: src/slic3r/GUI/Tab.cpp:3430 +msgid "BACK ARROW" +msgstr "REDEFINIR" + +#. TRN Description for "BACK ARROW" +#: src/slic3r/GUI/Tab.cpp:3432 +msgid "" +"indicates that the settings were changed and are not equal to the last saved " +"preset for the current option group.\n" +"Click the BACK ARROW icon to reset all settings for the current option group " +"to the last saved preset." +msgstr "" +"indica que as config. foram alteradas e não são iguais à última predefinição " +"salva para o grupo de opções atual.\n" +"Clique no ícone REDEFINIR para redefinir todas as config. do grupo de opções " +"atual para a última predefinição salva." + +#: src/slic3r/GUI/Tab.cpp:3442 +msgid "" +"LOCKED LOCK icon indicates that the settings are the same as the system (or " +"default) values for the current option group" +msgstr "" +"O ícone CADEADO FECHADO indica que as config. são as mesmas que os valores " +"do sistema (ou padrão) para o grupo de opções atual" + +#: src/slic3r/GUI/Tab.cpp:3444 +msgid "" +"UNLOCKED LOCK icon indicates that some settings were changed and are not " +"equal to the system (or default) values for the current option group.\n" +"Click to reset all settings for current option group to the system (or " +"default) values." +msgstr "" +"O ícone de CADEADO ABERTO indica que algumas config. foram alteradas e não " +"são iguais aos valores do sistema (ou padrão) para o grupo de opções atual.\n" +"Clique para redefinir todas as config. para o grupo de opções atual para os " +"valores do sistema (ou padrão)." + +#: src/slic3r/GUI/Tab.cpp:3447 +msgid "WHITE BULLET icon indicates a non system (or non default) preset." +msgstr "" +"O ícone PONTO BRANCO indica uma predefinição que não é do sistema (ou não " +"predefinida)." + +#: src/slic3r/GUI/Tab.cpp:3450 +msgid "" +"WHITE BULLET icon indicates that the settings are the same as in the last " +"saved preset for the current option group." +msgstr "" +"O ícone PONTO BRANCO indica que as config. são as mesmas da última " +"predefinição salva para o grupo de opções atual." + +#: src/slic3r/GUI/Tab.cpp:3452 +msgid "" +"BACK ARROW icon indicates that the settings were changed and are not equal " +"to the last saved preset for the current option group.\n" +"Click to reset all settings for the current option group to the last saved " +"preset." +msgstr "" +"O ícone de REDEFINIR indica que as config. foram alteradas e não são iguais " +"à última predefinição salva para o grupo de opções atual.\n" +"Clique para redefinir todas as config. do grupo de opções atual para a " +"última predefinição salva." + +#: src/slic3r/GUI/Tab.cpp:3458 +msgid "" +"LOCKED LOCK icon indicates that the value is the same as the system (or " +"default) value." +msgstr "" +"O ícone CADEADO FECHADO indica que o valor é o mesmo que o valor do sistema " +"(ou padrão)." + +#: src/slic3r/GUI/Tab.cpp:3459 +msgid "" +"UNLOCKED LOCK icon indicates that the value was changed and is not equal to " +"the system (or default) value.\n" +"Click to reset current value to the system (or default) value." +msgstr "" +"O ícone de CADEADO ABERTO indica que o valor foi alterado e não é igual ao " +"valor do sistema (ou padrão).\n" +"Clique para redefinir o valor atual para o valor do sistema (ou padrão)." + +#: src/slic3r/GUI/Tab.cpp:3465 +msgid "" +"WHITE BULLET icon indicates that the value is the same as in the last saved " +"preset." +msgstr "" +"O ícone PONTO BRANCO indica que o valor é o mesmo da última predefinição " +"guardada." + +#: src/slic3r/GUI/Tab.cpp:3466 +msgid "" +"BACK ARROW icon indicates that the value was changed and is not equal to the " +"last saved preset.\n" +"Click to reset current value to the last saved preset." +msgstr "" +"O ícone de REDEFINIR indica que o valor foi alterado e não é igual à última " +"predefinição salva.\n" +"Clique para redefinir o valor atual para a última predefinição salva." + +#. TRN Preset +#: src/slic3r/GUI/Tab.cpp:3579 +#, c-format +msgid "Save %s as:" +msgstr "Salvar %s como:" + +#: src/slic3r/GUI/Tab.cpp:3623 +msgid "the following suffix is not allowed:" +msgstr "o sufixo seguinte não é permitido:" + +#: src/slic3r/GUI/Tab.cpp:3627 +msgid "The supplied name is not available." +msgstr "O nome fornecido não está disponível." + +#: src/slic3r/GUI/Tab.cpp:3640 +msgid "Material" +msgstr "Material" + +#: src/slic3r/GUI/Tab.cpp:3642 src/slic3r/GUI/Tab.cpp:3733 +#: src/slic3r/GUI/wxExtensions.cpp:482 +msgid "Layers" +msgstr "Camadas" + +#: src/slic3r/GUI/Tab.cpp:3741 +msgid "Support head" +msgstr "Cabeça de suporte" + +#: src/slic3r/GUI/Tab.cpp:3746 +msgid "Support pillar" +msgstr "Pilar de suporte" + +#: src/slic3r/GUI/Tab.cpp:3760 +msgid "Connection of the support sticks and junctions" +msgstr "Conexão das varas de suporte e junções" + +#: src/slic3r/GUI/Tab.cpp:3765 +msgid "Automatic generation" +msgstr "Geração Automática" + +#: src/slic3r/GUI/Tab.hpp:328 src/slic3r/GUI/Tab.hpp:428 +msgid "Print Settings" +msgstr "Config. de impressão" + +#: src/slic3r/GUI/Tab.hpp:353 +msgid "Filament Settings" +msgstr "Config. de filamento" + +#: src/slic3r/GUI/Tab.hpp:389 +msgid "Printer Settings" +msgstr "Config. da impressora" + +#: src/slic3r/GUI/Tab.hpp:413 +msgid "Material Settings" +msgstr "Config. de material" + +#: src/slic3r/GUI/Tab.hpp:440 +msgid "Save preset" +msgstr "Salvar predefinição" + +#: src/slic3r/GUI/UpdateDialogs.cpp:38 +msgid "Update available" +msgstr "Atualização disponível" + +#: src/slic3r/GUI/UpdateDialogs.cpp:38 +#, c-format +msgid "New version of %s is available" +msgstr "Nova versão do %s está disponível" + +#: src/slic3r/GUI/UpdateDialogs.cpp:45 +msgid "Current version:" +msgstr "Versão atual:" + +#: src/slic3r/GUI/UpdateDialogs.cpp:47 +msgid "New version:" +msgstr "Nova versão:" + +#: src/slic3r/GUI/UpdateDialogs.cpp:55 +msgid "Changelog && Download" +msgstr "Changelog && Download" + +#: src/slic3r/GUI/UpdateDialogs.cpp:62 src/slic3r/GUI/UpdateDialogs.cpp:127 +msgid "Open changelog page" +msgstr "Abra a página do changelog" + +#: src/slic3r/GUI/UpdateDialogs.cpp:67 +msgid "Open download page" +msgstr "Abrir página de download" + +#: src/slic3r/GUI/UpdateDialogs.cpp:73 +msgid "Don't notify about new releases any more" +msgstr "Não notifique mais sobre novas versões" + +#: src/slic3r/GUI/UpdateDialogs.cpp:91 src/slic3r/GUI/UpdateDialogs.cpp:207 +msgid "Configuration update" +msgstr "Atualização de config." + +#: src/slic3r/GUI/UpdateDialogs.cpp:91 +msgid "Configuration update is available" +msgstr "A atualização de config. está disponível" + +#: src/slic3r/GUI/UpdateDialogs.cpp:94 +msgid "" +"Would you like to install it?\n" +"\n" +"Note that a full configuration snapshot will be created first. It can then " +"be restored at any time should there be a problem with the new version.\n" +"\n" +"Updated configuration bundles:" +msgstr "" +"Gostaria de instalá-lo?\n" +"\n" +"Observe que uma captura da config. completa será criado primeiro. Ele pode " +"então ser restaurado a qualquer momento se houver um problema com a nova " +"versão.\n" +"\n" +"Pacotes de config. atualizados:" + +#: src/slic3r/GUI/UpdateDialogs.cpp:115 +msgid "Comment:" +msgstr "Comentário:" + +#: src/slic3r/GUI/UpdateDialogs.cpp:151 +#, c-format +msgid "%s incompatibility" +msgstr "%s incompatibilidade" + +#: src/slic3r/GUI/UpdateDialogs.cpp:152 +#, c-format +msgid "%s configuration is incompatible" +msgstr "%s config. é incompatível" + +#: src/slic3r/GUI/UpdateDialogs.cpp:157 +#, c-format +msgid "" +"This version of %s is not compatible with currently installed configuration " +"bundles.\n" +"This probably happened as a result of running an older %s after using a " +"newer one.\n" +"\n" +"You may either exit %s and try again with a newer version, or you may re-run " +"the initial configuration. Doing so will create a backup snapshot of the " +"existing configuration before installing files compatible with this %s.\n" +msgstr "" +"Esta versão do %s não é compatível com pacotes de config. atualmente " +"instalados.\n" +"Isso provavelmente aconteceu como resultado da execução de um %s mais antigo " +"depois de usar um mais recente.\n" +"\n" +"Você pode sair %s e tente novamente com uma versão mais recente, ou você " +"pode executar novamente a config. inicial. Isso criará um instantâneo de " +"backup da config. existente antes de instalar os arquivos compatíveis com " +"este %s.\n" + +#: src/slic3r/GUI/UpdateDialogs.cpp:166 +#, c-format +msgid "This %s version: %s" +msgstr "Esta versão %s : %s" + +#: src/slic3r/GUI/UpdateDialogs.cpp:171 +msgid "Incompatible bundles:" +msgstr "Pacotes incompatíveis:" + +#: src/slic3r/GUI/UpdateDialogs.cpp:187 +#, c-format +msgid "Exit %s" +msgstr "Saída %s" + +#: src/slic3r/GUI/UpdateDialogs.cpp:190 +msgid "Re-configure" +msgstr "Re-config.urar" + +#: src/slic3r/GUI/UpdateDialogs.cpp:211 +#, c-format +msgid "" +"%s now uses an updated configuration structure.\n" +"\n" +"So called 'System presets' have been introduced, which hold the built-in " +"default settings for various printers. These System presets cannot be " +"modified, instead, users now may create their own presets inheriting " +"settings from one of the System presets.\n" +"An inheriting preset may either inherit a particular value from its parent " +"or override it with a customized value.\n" +"\n" +"Please proceed with the %s that follows to set up the new presets and to " +"choose whether to enable automatic preset updates." +msgstr "" +"%s agora usa uma estrutura de config. atualizada.\n" +"\n" +"Assim chamado ' Predefinições do sistema ' foram introduzidas, que mantêm as " +"config. padrão internas para várias impressoras. Essas predefinições do " +"sistema não podem ser modificadas, em vez disso, os usuários agora podem " +"criar suas próprias predefinições herdando as config. de uma das " +"predefinições do sistema.\n" +"Uma predefinição herdada pode herdar um valor específico de seu pai ou " +"substituí-lo por um valor personalizado.\n" +"\n" +"Por favor, prossiga com o %s que se segue para config.urar as novas " +"predefinições e para escolher se deseja ativar as atualizações automáticas " +"predefinidas." + +#: src/slic3r/GUI/UpdateDialogs.cpp:227 +msgid "For more information please visit our wiki page:" +msgstr "Para mais informações, visite a nossa página wiki:" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:14 +msgid "Ramming customization" +msgstr "Personalização de Ramming" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:40 +msgid "" +"Ramming denotes the rapid extrusion just before a tool change in a single-" +"extruder MM printer. Its purpose is to properly shape the end of the " +"unloaded filament so it does not prevent insertion of the new filament and " +"can itself be reinserted later. This phase is important and different " +"materials can require different extrusion speeds to get the good shape. For " +"this reason, the extrusion rates during ramming are adjustable.\n" +"\n" +"This is an expert-level setting, incorrect adjustment will likely lead to " +"jams, extruder wheel grinding into filament etc." +msgstr "" +"O Ramming denota a extrusão rápida apenas antes que uma mudança da " +"ferramenta em uma única-extrusora a impressora de multifilamentos Sua " +"finalidade é moldar corretamente a extremidade do filamento descarregado " +"assim que não impede a inserção do filamento novo e pode próprio ser " +"reintroduzido mais tarde. Esta fase é importante e os materiais diferentes " +"podem exigir velocidades diferentes da extrusão para começ a boa forma. Por " +"esta razão, as taxas de extrusão durante a batendo são ajustáveis.\n" +"\n" +"Esta é uma config. de nível especialista, ajuste incorreto provavelmente " +"levará a compotas, roda extrusora moagem em filamento etc." + +#: src/slic3r/GUI/WipeTowerDialog.cpp:82 +msgid "Total ramming time" +msgstr "Tempo total de Ramming" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:84 +msgid "Total rammed volume" +msgstr "Volume total de Ramming" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:88 +msgid "Ramming line width" +msgstr "Largura da linha de Ramming" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:90 +msgid "Ramming line spacing" +msgstr "Espaçamento de linha de Ramming" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:141 +msgid "Wipe tower - Purging volume adjustment" +msgstr "Torre de limpeza - Ajuste de volume de purga" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:225 +msgid "" +"Here you can adjust required purging volume (mm³) for any given pair of " +"tools." +msgstr "" +"Aqui você pode ajustar o volume de purga necessário (mm ³) para qualquer par " +"dado de ferramentas." + +#: src/slic3r/GUI/WipeTowerDialog.cpp:226 +msgid "Extruder changed to" +msgstr "Extrusora alterada para" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:234 +msgid "unloaded" +msgstr "descarregado" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:235 +msgid "loaded" +msgstr "carregado" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:240 +msgid "Tool #" +msgstr "Ferramenta #" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:247 +msgid "" +"Total purging volume is calculated by summing two values below, depending on " +"which tools are loaded/unloaded." +msgstr "" +"O volume de purga total é calculado somando-se dois valores abaixo, " +"dependendo de quais ferramentas são carregadas/descarregadas." + +#: src/slic3r/GUI/WipeTowerDialog.cpp:248 +msgid "Volume to purge (mm³) when the filament is being" +msgstr "Volume a purgar (mm ³) quando o filamento está a ser" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:262 +msgid "From" +msgstr "De" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:327 +msgid "" +"Switching to simple settings will discard changes done in the advanced " +"mode!\n" +"\n" +"Do you want to proceed?" +msgstr "" +"Mudar para config. simples irá descartar as alterações feitas no modo " +"avançado!\n" +"\n" +"Você quer prosseguir?" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:339 +msgid "Show simplified settings" +msgstr "Mostrar config. simplificadas" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:339 +msgid "Show advanced settings" +msgstr "Mostrar opções avançadas" + +#: src/slic3r/GUI/wxExtensions.cpp:471 +msgid "Instances" +msgstr "Instâncias" + +#: src/slic3r/GUI/wxExtensions.cpp:475 src/slic3r/GUI/wxExtensions.cpp:619 +#, c-format +msgid "Instance %d" +msgstr "Instância %d" + +#: src/slic3r/GUI/wxExtensions.cpp:509 +msgid "Range" +msgstr "Intervalo" + +#: src/slic3r/GUI/wxExtensions.cpp:2731 +msgid "One layer mode" +msgstr "Modo de uma camada" + +#: src/slic3r/GUI/wxExtensions.cpp:2732 +msgid "Add/Del color change" +msgstr "Add/Excluir mudança de cor" + +#: src/slic3r/GUI/wxExtensions.cpp:2733 +msgid "Discard all color changes" +msgstr "Descartar todas as alterações de cor" + +#: src/slic3r/GUI/wxExtensions.cpp:2993 +#, c-format +msgid "Switch to the %s mode" +msgstr "Mude para o modo %s" + +#: src/slic3r/GUI/wxExtensions.cpp:2994 +#, c-format +msgid "Current mode is %s" +msgstr "O modo atual é %s" + +#: src/slic3r/Utils/Duet.cpp:51 +msgid "Connection to Duet works correctly." +msgstr "A conexão com o Duet funciona corretamente." + +#: src/slic3r/Utils/Duet.cpp:56 +msgid "Could not connect to Duet" +msgstr "Não foi possível conectar-se ao Duet" + +#: src/slic3r/Utils/Duet.cpp:84 src/slic3r/Utils/Duet.cpp:154 +msgid "Unknown error occured" +msgstr "Ocorreu um erro desconhecido" + +#: src/slic3r/Utils/Duet.cpp:148 +msgid "Wrong password" +msgstr "Senha incorreta" + +#: src/slic3r/Utils/Duet.cpp:151 +msgid "Could not get resources to create a new connection" +msgstr "Não foi possível obter recursos para criar uma nova conexão" + +#: src/slic3r/Utils/OctoPrint.cpp:70 +#, c-format +msgid "Mismatched type of print host: %s" +msgstr "Tipo incompatível de host de impressão: %s" + +#: src/slic3r/Utils/OctoPrint.cpp:85 +msgid "Connection to OctoPrint works correctly." +msgstr "A ligação ao OctoPrint funciona correctamente." + +#: src/slic3r/Utils/OctoPrint.cpp:91 +msgid "Could not connect to OctoPrint" +msgstr "Não foi possível conectar-se ao OctoPrint" + +#: src/slic3r/Utils/OctoPrint.cpp:91 +msgid "Note: OctoPrint version at least 1.1.0 is required." +msgstr "Nota: OctoPrint versão pelo menos 1.1.0 é necessária." + +#: src/slic3r/Utils/OctoPrint.cpp:196 +msgid "Connection to Prusa SL1 works correctly." +msgstr "A conexão com o Prusa SL1 funciona corretamente." + +#: src/slic3r/Utils/OctoPrint.cpp:201 +msgid "Could not connect to Prusa SLA" +msgstr "Não foi possível conectar-se a Prusa SLA" + +#: src/slic3r/Utils/PresetUpdater.cpp:614 +#, c-format +msgid "requires min. %s and max. %s" +msgstr "requer min . %s e máx. %s" + +#: src/slic3r/Utils/PresetUpdater.cpp:619 +#, c-format +msgid "requires min. %s" +msgstr "requer min . %s" + +#: src/slic3r/Utils/PresetUpdater.cpp:621 +#, c-format +msgid "requires max. %s" +msgstr "requer Max. %s" + +#: src/slic3r/Utils/FixModelByWin10.cpp:219 +#: src/slic3r/Utils/FixModelByWin10.cpp:359 +msgid "Exporting source model" +msgstr "Exportando o modelo de origem" + +#: src/slic3r/Utils/FixModelByWin10.cpp:235 +msgid "Failed loading the input model." +msgstr "Falha ao carregar o modelo de entrada." + +#: src/slic3r/Utils/FixModelByWin10.cpp:242 +msgid "Repairing model by the Netfabb service" +msgstr "Modelo de reparação pelo serviço Netfabb" + +#: src/slic3r/Utils/FixModelByWin10.cpp:248 +msgid "Mesh repair failed." +msgstr "Falha na reparação de malha." + +#: src/slic3r/Utils/FixModelByWin10.cpp:251 +#: src/slic3r/Utils/FixModelByWin10.cpp:378 +msgid "Loading repaired model" +msgstr "Carregando o modelo reparado" + +#: src/slic3r/Utils/FixModelByWin10.cpp:263 +#: src/slic3r/Utils/FixModelByWin10.cpp:270 +#: src/slic3r/Utils/FixModelByWin10.cpp:302 +msgid "Saving mesh into the 3MF container failed." +msgstr "Falha ao salvar a malha 3MF no contêiner." + +#: src/slic3r/Utils/FixModelByWin10.cpp:340 +msgid "Model fixing" +msgstr "Fixação do modelo" + +#: src/slic3r/Utils/FixModelByWin10.cpp:341 +msgid "Exporting model..." +msgstr "Exportando o modelo..." + +#: src/slic3r/Utils/FixModelByWin10.cpp:368 +msgid "Export of a temporary 3mf file failed" +msgstr "Falha na exportação de um arquivo 3mf temporário" + +#: src/slic3r/Utils/FixModelByWin10.cpp:383 +msgid "Import of the repaired 3mf file failed" +msgstr "Falha na importação do arquivo 3mf reparado" + +#: src/slic3r/Utils/FixModelByWin10.cpp:385 +msgid "Repaired 3MF file does not contain any object" +msgstr "O arquivo 3MF reparado não contém nenhum objeto" + +#: src/slic3r/Utils/FixModelByWin10.cpp:387 +msgid "Repaired 3MF file contains more than one object" +msgstr "O arquivo 3MF reparado contém mais de um objeto" + +#: src/slic3r/Utils/FixModelByWin10.cpp:389 +msgid "Repaired 3MF file does not contain any volume" +msgstr "O arquivo 3MF reparado não contém nenhum volume" + +#: src/slic3r/Utils/FixModelByWin10.cpp:391 +msgid "Repaired 3MF file contains more than one volume" +msgstr "O arquivo 3MF reparado contém mais de um volume" + +#: src/slic3r/Utils/FixModelByWin10.cpp:400 +msgid "Model repair finished" +msgstr "Reparo do modelo terminado" + +#: src/slic3r/Utils/FixModelByWin10.cpp:406 +msgid "Model repair canceled" +msgstr "Reparo do modelo cancelado" + +#: src/slic3r/Utils/FixModelByWin10.cpp:423 +msgid "Model repaired successfully" +msgstr "Modelo reparado com sucesso" + +#: src/slic3r/Utils/FixModelByWin10.cpp:423 +#: src/slic3r/Utils/FixModelByWin10.cpp:426 +msgid "Model Repair by the Netfabb service" +msgstr "Reparação de modelos pelo serviço Netfabb" + +#: src/slic3r/Utils/FixModelByWin10.cpp:426 +msgid "Model repair failed: \n" +msgstr "Falha no reparo do modelo:\n" + +#: src/libslic3r/Zipper.cpp:32 +msgid "undefined error" +msgstr "erro indefinido" + +#: src/libslic3r/Zipper.cpp:34 +msgid "too many files" +msgstr "muitos arquivos" + +#: src/libslic3r/Zipper.cpp:36 +msgid "file too large" +msgstr "arquivo muito grande" + +#: src/libslic3r/Zipper.cpp:38 +msgid "unsupported method" +msgstr "método não suportado" + +#: src/libslic3r/Zipper.cpp:40 +msgid "unsupported encryption" +msgstr "criptografia sem suporte" + +#: src/libslic3r/Zipper.cpp:42 +msgid "unsupported feature" +msgstr "recurso não suportado" + +#: src/libslic3r/Zipper.cpp:44 +msgid "failed finding central directory" +msgstr "falha ao encontrar o diretório central" + +#: src/libslic3r/Zipper.cpp:46 +msgid "not a ZIP archive" +msgstr "não um arquivo ZIP" + +#: src/libslic3r/Zipper.cpp:48 +msgid "invalid header or archive is corrupted" +msgstr "cabeçalho ou arquivo inválido está corrompido" + +#: src/libslic3r/Zipper.cpp:50 +msgid "unsupported multidisk archive" +msgstr "arquivo Multidisk sem suporte" + +#: src/libslic3r/Zipper.cpp:52 +msgid "decompression failed or archive is corrupted" +msgstr "descompressão falhou ou arquivo está corrompido" + +#: src/libslic3r/Zipper.cpp:54 +msgid "compression failed" +msgstr "falha na compactação" + +#: src/libslic3r/Zipper.cpp:56 +msgid "unexpected decompressed size" +msgstr "tamanho descomprimido inesperado" + +#: src/libslic3r/Zipper.cpp:58 +msgid "CRC-32 check failed" +msgstr "Verificação CRC-32 falhou" + +#: src/libslic3r/Zipper.cpp:60 +msgid "unsupported central directory size" +msgstr "tamanho do diretório central não suportado" + +#: src/libslic3r/Zipper.cpp:62 +msgid "allocation failed" +msgstr "alocação falhou" + +#: src/libslic3r/Zipper.cpp:64 +msgid "file open failed" +msgstr "falha na abertura do arquivo" + +#: src/libslic3r/Zipper.cpp:66 +msgid "file create failed" +msgstr "falha na criação do arquivo" + +#: src/libslic3r/Zipper.cpp:68 +msgid "file write failed" +msgstr "falha na gravação do arquivo" + +#: src/libslic3r/Zipper.cpp:70 +msgid "file read failed" +msgstr "falha na leitura do arquivo" + +#: src/libslic3r/Zipper.cpp:72 +msgid "file close failed" +msgstr "falha ao fechar o arquivo" + +#: src/libslic3r/Zipper.cpp:74 +msgid "file seek failed" +msgstr "falha na busca de arquivo" + +#: src/libslic3r/Zipper.cpp:76 +msgid "file stat failed" +msgstr "falha no status do arquivo" + +#: src/libslic3r/Zipper.cpp:78 +msgid "invalid parameter" +msgstr "parâmetro inválido" + +#: src/libslic3r/Zipper.cpp:80 +msgid "invalid filename" +msgstr "nome de arquivo inválido" + +#: src/libslic3r/Zipper.cpp:82 +msgid "buffer too small" +msgstr "buffer muito pequeno" + +#: src/libslic3r/Zipper.cpp:84 +msgid "internal error" +msgstr "erro interno" + +#: src/libslic3r/Zipper.cpp:86 +msgid "file not found" +msgstr "arquivo não encontrado" + +#: src/libslic3r/Zipper.cpp:88 +msgid "archive is too large" +msgstr "arquivo é muito grande" + +#: src/libslic3r/Zipper.cpp:90 +msgid "validation failed" +msgstr "falha na validação" + +#: src/libslic3r/Zipper.cpp:92 +msgid "write calledback failed" +msgstr "write calledback falhou" + +#: src/libslic3r/Zipper.cpp:102 +msgid "Error with zip archive" +msgstr "Erro com arquivo zip" + +#: src/libslic3r/Print.cpp:1112 +msgid "All objects are outside of the print volume." +msgstr "Todos os objetos estão fora do volume de impressão." + +#: src/libslic3r/Print.cpp:1139 +msgid "Some objects are too close; your extruder will collide with them." +msgstr "Alguns objetos são muito próximos; sua extrusora irá colidir com eles." + +#: src/libslic3r/Print.cpp:1154 +msgid "" +"Some objects are too tall and cannot be printed without extruder collisions." +msgstr "" +"Alguns objetos são muito altos e não podem ser impressos sem colisões de " +"extrusoras." + +#: src/libslic3r/Print.cpp:1164 +msgid "The Spiral Vase option can only be used when printing a single object." +msgstr "A opção vaso espiral só pode ser usada ao imprimir um único objeto." + +#: src/libslic3r/Print.cpp:1171 +msgid "" +"The Spiral Vase option can only be used when printing single material " +"objects." +msgstr "" +"A opção vaso espiral só pode ser usada ao imprimir objetos de material único." + +#: src/libslic3r/Print.cpp:1184 +msgid "" +"The wipe tower is only supported if all extruders have the same nozzle " +"diameter and use filaments of the same diameter." +msgstr "" +"A torre de limpeza só é suportada se todas as extrusoras tiverem o mesmo " +"diâmetro da ponteira e usarem filamentos do mesmo diâmetro." + +#: src/libslic3r/Print.cpp:1189 +msgid "" +"The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter " +"and Repetier G-code flavors." +msgstr "" +"A Wipe Tower é atualmente suportada apenas para os firmwares Marlin, RepRap/" +"Sprinter e Repetier G-code." + +#: src/libslic3r/Print.cpp:1191 +msgid "" +"The Wipe Tower is currently only supported with the relative extruder " +"addressing (use_relative_e_distances=1)." +msgstr "" +"A torre da limpeza é suportada atualmente somente com o endereçamento " +"relativo da extrusora (use_relative_e_distances = 1)." + +#: src/libslic3r/Print.cpp:1193 +msgid "Ooze prevention is currently not supported with the wipe tower enabled." +msgstr "" +"A prevenção de escorrimento não é suportada atualmente com a torre da " +"limpeza permitida." + +#: src/libslic3r/Print.cpp:1214 +msgid "" +"The Wipe Tower is only supported for multiple objects if they have equal " +"layer heights" +msgstr "" +"A torre de limpeza só é suportada para vários objetos se eles tiverem " +"alturas de camada iguais" + +#: src/libslic3r/Print.cpp:1216 +msgid "" +"The Wipe Tower is only supported for multiple objects if they are printed " +"over an equal number of raft layers" +msgstr "" +"A torre de limpeza só é suportada para vários objetos se elas forem " +"impressas em um número igual de camadas de estrado" + +#: src/libslic3r/Print.cpp:1218 +msgid "" +"The Wipe Tower is only supported for multiple objects if they are printed " +"with the same support_material_contact_distance" +msgstr "" +"A torre de limpeza só é suportado para vários objetos se eles são impressos " +"com a mesma distância de contato do suporte" + +#: src/libslic3r/Print.cpp:1220 +msgid "" +"The Wipe Tower is only supported for multiple objects if they are sliced " +"equally." +msgstr "" +"A torre de limpeza só é suportada para vários objetos se eles são fatiados " +"igualmente." + +#: src/libslic3r/Print.cpp:1248 +msgid "" +"The Wipe tower is only supported if all objects have the same layer height " +"profile" +msgstr "" +"A torre de limpeza só é suportada se todos os objetos tiverem o mesmo perfil " +"de altura da camada" + +#: src/libslic3r/Print.cpp:1258 +msgid "The supplied settings will cause an empty print." +msgstr "As config. fornecidas causarão uma impressão vazia." + +#: src/libslic3r/Print.cpp:1275 +msgid "" +"One or more object were assigned an extruder that the printer does not have." +msgstr "" +"Um ou mais objetos foram atribuídos a uma extrusora que a impressora não tem." + +#: src/libslic3r/Print.cpp:1284 +msgid "%1%=%2% mm is too low to be printable at a layer height %3% mm" +msgstr "" +"%1% = %2% mm é muito baixo para ser impresso a uma altura de camada %3% mm" + +#: src/libslic3r/Print.cpp:1287 +msgid "Excessive %1%=%2% mm to be printable with a nozzle diameter %3% mm" +msgstr "" +"Excesso %1%=%2% milímetro a ser imprimível com um diâmetro da ponteira %3% " +"milímetro" + +#: src/libslic3r/Print.cpp:1298 +msgid "" +"Printing with multiple extruders of differing nozzle diameters. If support " +"is to be printed with the current extruder (support_material_extruder == 0 " +"or support_material_interface_extruder == 0), all nozzles have to be of the " +"same diameter." +msgstr "" +"Impressão com múltiplas extrusoras de diferentes diâmetros de bicos. Se a " +"sustentação deve ser imprimida com a extrusora atual " +"(support_material_extruder = = 0 ou support_material_interface_extruder = = " +"0), todos as ponteiras têm que ser do mesmo diâmetro." + +#: src/libslic3r/Print.cpp:1306 +msgid "" +"For the Wipe Tower to work with the soluble supports, the support layers " +"need to be synchronized with the object layers." +msgstr "" +"Para que a torre de limpeza funcione com os suportes solúveis, as camadas de " +"suporte precisam ser sincronizadas com as camadas de objeto." + +#: src/libslic3r/Print.cpp:1310 +msgid "" +"The Wipe Tower currently supports the non-soluble supports only if they are " +"printed with the current extruder without triggering a tool change. (both " +"support_material_extruder and support_material_interface_extruder need to be " +"set to 0)." +msgstr "" +"A torre de limpeza suporta atualmente os suportes não-solúveis somente se " +"são imprimidos com o extrusor atual sem provocar uma mudança da ferramenta. " +"(ambos support_material_extruder e support_material_interface_extruder " +"precisam ser definidos como 0)." + +#: src/libslic3r/Print.cpp:1332 +msgid "First layer height can't be greater than nozzle diameter" +msgstr "" +"A primeira altura da camada não pode ser maior do que o diâmetro da ponteira" + +#: src/libslic3r/Print.cpp:1337 +msgid "Layer height can't be greater than nozzle diameter" +msgstr "A altura da camada não pode ser maior do que o diâmetro da ponteira" + +#: src/libslic3r/Print.cpp:1492 +msgid "Infilling layers" +msgstr "Camadas de preenchimento" + +#: src/libslic3r/Print.cpp:1500 +msgid "Generating skirt" +msgstr "Gerando saia" + +#: src/libslic3r/Print.cpp:1508 +msgid "Generating brim" +msgstr "Gerando a aba" + +#: src/libslic3r/Print.cpp:1536 +msgid "Exporting G-code" +msgstr "Exportando o G-code" + +#: src/libslic3r/Print.cpp:1540 +msgid "Generating G-code" +msgstr "Gerando G-code" + +#: src/libslic3r/SLAPrint.cpp:64 +msgid "Slicing model" +msgstr "Modelo de fatiamento" + +#: src/libslic3r/SLAPrint.cpp:65 src/libslic3r/SLAPrint.cpp:899 +msgid "Generating support points" +msgstr "Gerando pontos de suporte" + +#: src/libslic3r/SLAPrint.cpp:66 +msgid "Generating support tree" +msgstr "Gerando suporte em árvore" + +#: src/libslic3r/SLAPrint.cpp:67 +msgid "Generating pad" +msgstr "Gerando pad" + +#: src/libslic3r/SLAPrint.cpp:68 +msgid "Slicing supports" +msgstr "Fatiando suportes" + +#: src/libslic3r/SLAPrint.cpp:85 +msgid "Merging slices and calculating statistics" +msgstr "Mesclando camadas e calculando estatísticas" + +#: src/libslic3r/SLAPrint.cpp:86 +msgid "Rasterizing layers" +msgstr "Rasterizando camadas" + +#: src/libslic3r/SLAPrint.cpp:661 +msgid "" +"Cannot proceed without support points! Add support points or disable support " +"generation." +msgstr "" +"Não pode prosseguir sem pontos de suporte! Adicione pontos de suporte ou " +"desative a geração de suporte." + +#: src/libslic3r/SLAPrint.cpp:678 +msgid "" +"Elevation is too low for object. Use the \"Pad around object\" feature to " +"print the object without elevation." +msgstr "" +"A elevação é muito baixa para o objeto. Use o recurso \"pad ao redor do " +"objeto\" para imprimir o objeto sem elevação." + +#: src/libslic3r/SLAPrint.cpp:684 +msgid "" +"The endings of the support pillars will be deployed on the gap between the " +"object and the pad. 'Support base safety distance' has to be greater than " +"the 'Pad object gap' parameter to avoid this." +msgstr "" +"As terminações dos pilares de suporte serão implantadas na lacuna entre o " +"objeto e o pad. ' Distância de segurança de base de suporte ' tem de ser " +"maior do que o parâmetro ' pad objecto Gap ' para evitar este." + +#: src/libslic3r/SLAPrint.cpp:696 +msgid "Exposition time is out of printer profile bounds." +msgstr "O tempo de exposição está fora dos limites do perfil da impressora." + +#: src/libslic3r/SLAPrint.cpp:703 +msgid "Initial exposition time is out of printer profile bounds." +msgstr "" +"O tempo de exposição inicial está fora dos limites do perfil da impressora." + +#: src/libslic3r/SLAPrint.cpp:787 +msgid "" +"Slicing had to be stopped due to an internal error: Inconsistent slice index." +msgstr "" +"O fatiamento teve que ser parado devido a um erro interno: índice de " +"fatiamento inconsistente." + +#: src/libslic3r/SLAPrint.cpp:982 src/libslic3r/SLAPrint.cpp:992 +#: src/libslic3r/SLAPrint.cpp:1033 +msgid "Visualizing supports" +msgstr "Visualizando suportes" + +#: src/libslic3r/SLAPrint.cpp:1566 +msgid "Slicing done" +msgstr "Fatiamento pronto" + +#: src/libslic3r/PrintBase.cpp:71 +msgid "Failed processing of the output_filename_format template." +msgstr "Falha no processamento do modelo output_filename_format." + +#: src/libslic3r/PrintConfig.cpp:43 src/libslic3r/PrintConfig.cpp:44 +msgid "Printer technology" +msgstr "Tecnologia da impressora" + +#: src/libslic3r/PrintConfig.cpp:51 +msgid "Bed shape" +msgstr "Formato da mesa" + +#: src/libslic3r/PrintConfig.cpp:56 +msgid "Bed custom texture" +msgstr "Textura customizada da mesa" + +#: src/libslic3r/PrintConfig.cpp:61 +msgid "Bed custom model" +msgstr "Modelo customizado da mesa" + +#: src/libslic3r/PrintConfig.cpp:68 +msgid "" +"This setting controls the height (and thus the total number) of the slices/" +"layers. Thinner layers give better accuracy but take more time to print." +msgstr "" +"Essa config. controla a altura (e, portanto, o número total) das fatias/" +"camadas. Camadas mais finas dão melhor precisão, mas levam mais tempo para " +"imprimir." + +#: src/libslic3r/PrintConfig.cpp:75 +msgid "Max print height" +msgstr "Altura máxima de impressão" + +#: src/libslic3r/PrintConfig.cpp:76 +msgid "" +"Set this to the maximum height that can be reached by your extruder while " +"printing." +msgstr "" +"Defina isto para a altura máxima que pode ser alcançada pela sua extrusora " +"durante a impressão." + +#: src/libslic3r/PrintConfig.cpp:82 +msgid "Slice gap closing radius" +msgstr "Raio de fechamento da abertura da fatia" + +#: src/libslic3r/PrintConfig.cpp:84 +msgid "" +"Cracks smaller than 2x gap closing radius are being filled during the " +"triangle mesh slicing. The gap closing operation may reduce the final print " +"resolution, therefore it is advisable to keep the value reasonably low." +msgstr "" +"As rachaduras menores do que duas vezes o raio de fechamento estão sendo " +"preenchidas durante o fatiamento da malha triangular. A operação de " +"fechamento de vão pode reduzir a resolução final de impressão, portanto, é " +"aconselhável manter o valor razoavelmente baixo." + +#: src/libslic3r/PrintConfig.cpp:92 +msgid "Hostname, IP or URL" +msgstr "Hostname, IP ou URL" + +#: src/libslic3r/PrintConfig.cpp:93 +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the hostname, IP address or URL of the printer host instance." +msgstr "" +"Slic3r pode carregar arquivos de G-code para um host de impressora. Este " +"campo deve conter o nome de host, o endereço IP ou a URL da instância de " +"host da impressora." + +#: src/libslic3r/PrintConfig.cpp:99 +msgid "API Key / Password" +msgstr "Chave de API/senha" + +#: src/libslic3r/PrintConfig.cpp:100 +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the API Key or the password required for authentication." +msgstr "" +"Slic3r pode carregar arquivos de G-code para um host de impressora. Este " +"campo deve conter a chave de API ou a senha exigida para a autenticação." + +#: src/libslic3r/PrintConfig.cpp:106 +msgid "HTTPS CA File" +msgstr "Arquivo de CA HTTPS" + +#: src/libslic3r/PrintConfig.cpp:107 +msgid "" +"Custom CA certificate file can be specified for HTTPS OctoPrint connections, " +"in crt/pem format. If left blank, the default OS CA certificate repository " +"is used." +msgstr "" +"O arquivo de certificado de CA personalizado pode ser especificado para " +"conexões HTTPS OctoPrint, no formato CRT/PEM. Se deixado em branco, o " +"repositório de certificados do OS CA padrão é usado." + +#: src/libslic3r/PrintConfig.cpp:121 +msgid "Avoid crossing perimeters" +msgstr "Evitar cruzamento de perímetros" + +#: src/libslic3r/PrintConfig.cpp:122 +msgid "" +"Optimize travel moves in order to minimize the crossing of perimeters. This " +"is mostly useful with Bowden extruders which suffer from oozing. This " +"feature slows down both the print and the G-code generation." +msgstr "" +"Otimize os movimentos de viagem para minimizar o cruzamento de perímetros. " +"Isto é principalmente útil com extrusoras Bowden que sofrem de escorrimento. " +"Este recurso retarda a impressão e a geração de G-code." + +#: src/libslic3r/PrintConfig.cpp:129 src/libslic3r/PrintConfig.cpp:2027 +msgid "Other layers" +msgstr "Outras camadas" + +#: src/libslic3r/PrintConfig.cpp:130 +msgid "" +"Bed temperature for layers after the first one. Set this to zero to disable " +"bed temperature control commands in the output." +msgstr "" +"Temperatura da mesa para camadas após o primeiro. Defina isso como zero para " +"desabilitar os comandos de controle de temperatura da mesa na saída." + +#: src/libslic3r/PrintConfig.cpp:132 +msgid "Bed temperature" +msgstr "Temperatura da mesa" + +#: src/libslic3r/PrintConfig.cpp:139 +msgid "" +"This custom code is inserted at every layer change, right before the Z move. " +"Note that you can use placeholder variables for all Slic3r settings as well " +"as [layer_num] and [layer_z]." +msgstr "" +"Esse código personalizado é inserido em cada alteração de camada, logo antes " +"da movimentação Z. Observe que você pode usar variáveis de espaço reservado " +"para todas as config. Slic3r, bem como [layer_num] e [layer_z]." + +#: src/libslic3r/PrintConfig.cpp:149 +msgid "Between objects G-code" +msgstr "G-code entre objetos" + +#: src/libslic3r/PrintConfig.cpp:150 +msgid "" +"This code is inserted between objects when using sequential printing. By " +"default extruder and bed temperature are reset using non-wait command; " +"however if M104, M109, M140 or M190 are detected in this custom code, Slic3r " +"will not add temperature commands. Note that you can use placeholder " +"variables for all Slic3r settings, so you can put a \"M109 " +"S[first_layer_temperature]\" command wherever you want." +msgstr "" +"Esse código é inserido entre objetos ao usar a impressão sequencial. Por " +"padrão, a extrusora e a temperatura da mesa são redefinidas usando o comando " +"não esperar; no entanto, se M104, M109, M140 ou M190 são detectados neste " +"código personalizado, Slic3r não adicionará comandos de temperatura. Observe " +"que você pode usar variáveis de espaço reservado para todas as config. de " +"Slic3r, para que você possa colocar um comando \"M109 S " +"[temperatura_primeira_camada]\" onde quiser." + +#: src/libslic3r/PrintConfig.cpp:161 +msgid "Number of solid layers to generate on bottom surfaces." +msgstr "Número de camadas sólidas para gerar em superfícies inferiores." + +#: src/libslic3r/PrintConfig.cpp:162 +msgid "Bottom solid layers" +msgstr "Camadas sólidas inferiores" + +#: src/libslic3r/PrintConfig.cpp:167 +msgid "Bridge" +msgstr "Ponte" + +#: src/libslic3r/PrintConfig.cpp:168 +msgid "" +"This is the acceleration your printer will use for bridges. Set zero to " +"disable acceleration control for bridges." +msgstr "" +"Esta é a aceleração que sua impressora usará para pontes. Defina zero para " +"desabilitar o controle de aceleração para pontes." + +#: src/libslic3r/PrintConfig.cpp:170 src/libslic3r/PrintConfig.cpp:313 +#: src/libslic3r/PrintConfig.cpp:840 src/libslic3r/PrintConfig.cpp:961 +#: src/libslic3r/PrintConfig.cpp:1130 src/libslic3r/PrintConfig.cpp:1183 +#: src/libslic3r/PrintConfig.cpp:1194 src/libslic3r/PrintConfig.cpp:1383 +msgid "mm/s²" +msgstr "mm/s²" + +#: src/libslic3r/PrintConfig.cpp:176 +msgid "Bridging angle" +msgstr "Ângulo de ponte" + +#: src/libslic3r/PrintConfig.cpp:178 +msgid "" +"Bridging angle override. If left to zero, the bridging angle will be " +"calculated automatically. Otherwise the provided angle will be used for all " +"bridges. Use 180° for zero angle." +msgstr "" +"Sobreposição de ângulo de ponte. Se deixado em zero, o ângulo de ponte será " +"calculado automaticamente. Caso contrário, o ângulo fornecido será usado " +"para todas as pontes. Use 180 ° para o ângulo zero." + +#: src/libslic3r/PrintConfig.cpp:181 src/libslic3r/PrintConfig.cpp:758 +#: src/libslic3r/PrintConfig.cpp:1619 src/libslic3r/PrintConfig.cpp:1629 +#: src/libslic3r/PrintConfig.cpp:1858 src/libslic3r/PrintConfig.cpp:2012 +#: src/libslic3r/PrintConfig.cpp:2197 src/libslic3r/PrintConfig.cpp:2614 +#: src/libslic3r/PrintConfig.cpp:2724 +msgid "°" +msgstr "°" + +#: src/libslic3r/PrintConfig.cpp:187 +msgid "Bridges fan speed" +msgstr "Velocidade da ventoinha nas pontes" + +#: src/libslic3r/PrintConfig.cpp:188 +msgid "This fan speed is enforced during all bridges and overhangs." +msgstr "" +"Esta velocidade da ventoinha é imposta durante todas as pontes e angulações." + +#: src/libslic3r/PrintConfig.cpp:189 src/libslic3r/PrintConfig.cpp:770 +#: src/libslic3r/PrintConfig.cpp:1203 src/libslic3r/PrintConfig.cpp:1266 +#: src/libslic3r/PrintConfig.cpp:1511 src/libslic3r/PrintConfig.cpp:2366 +#: src/libslic3r/PrintConfig.cpp:2654 +msgid "%" +msgstr "%" + +#: src/libslic3r/PrintConfig.cpp:196 +msgid "Bridge flow ratio" +msgstr "Relação de fluxo da ponte" + +#: src/libslic3r/PrintConfig.cpp:198 +msgid "" +"This factor affects the amount of plastic for bridging. You can decrease it " +"slightly to pull the extrudates and prevent sagging, although default " +"settings are usually good and you should experiment with cooling (use a fan) " +"before tweaking this." +msgstr "" +"Esse fator afeta a quantidade de plástico para a ponte. Você pode diminuí-lo " +"um pouco para puxar as extrusões e evitar a flacidez, embora as config. " +"padrão são geralmente boas e você deve experimentar com refrigeração (use " +"uma ventoinha) antes de ajustes isso." + +#: src/libslic3r/PrintConfig.cpp:208 +msgid "Bridges" +msgstr "Pontes" + +#: src/libslic3r/PrintConfig.cpp:210 +msgid "Speed for printing bridges." +msgstr "Velocidade para a impressão de pontes." + +#: src/libslic3r/PrintConfig.cpp:211 src/libslic3r/PrintConfig.cpp:592 +#: src/libslic3r/PrintConfig.cpp:600 src/libslic3r/PrintConfig.cpp:609 +#: src/libslic3r/PrintConfig.cpp:617 src/libslic3r/PrintConfig.cpp:644 +#: src/libslic3r/PrintConfig.cpp:663 src/libslic3r/PrintConfig.cpp:899 +#: src/libslic3r/PrintConfig.cpp:1026 src/libslic3r/PrintConfig.cpp:1112 +#: src/libslic3r/PrintConfig.cpp:1148 src/libslic3r/PrintConfig.cpp:1161 +#: src/libslic3r/PrintConfig.cpp:1172 src/libslic3r/PrintConfig.cpp:1225 +#: src/libslic3r/PrintConfig.cpp:1284 src/libslic3r/PrintConfig.cpp:1412 +#: src/libslic3r/PrintConfig.cpp:1586 src/libslic3r/PrintConfig.cpp:1595 +#: src/libslic3r/PrintConfig.cpp:1991 src/libslic3r/PrintConfig.cpp:2104 +msgid "mm/s" +msgstr "mm/s" + +#: src/libslic3r/PrintConfig.cpp:218 +msgid "Brim width" +msgstr "Largura da aba" + +#: src/libslic3r/PrintConfig.cpp:219 +msgid "" +"Horizontal width of the brim that will be printed around each object on the " +"first layer." +msgstr "" +"Largura horizontal da aba que será impressa em torno de cada objeto na " +"primeira camada." + +#: src/libslic3r/PrintConfig.cpp:226 +msgid "Clip multi-part objects" +msgstr "Clip objetos de várias partes" + +#: src/libslic3r/PrintConfig.cpp:227 +msgid "" +"When printing multi-material objects, this settings will make Slic3r to clip " +"the overlapping object parts one by the other (2nd part will be clipped by " +"the 1st, 3rd part will be clipped by the 1st and 2nd etc)." +msgstr "" +"Ao imprimir objetos de vários materiais, essas config. farão com que o " +"Slic3r recorte as partes do objeto sobrepostas uma pela outra (2ª parte será " +"cortada pela 1ª, 3ª parte será cortada pela 1ª e 2ª, etc.)." + +#: src/libslic3r/PrintConfig.cpp:234 +msgid "Colorprint height" +msgstr "Altura da impressão colorida" + +#: src/libslic3r/PrintConfig.cpp:235 +msgid "Heights at which a filament change is to occur." +msgstr "Alturas em que uma mudança do filamento ocorre." + +#: src/libslic3r/PrintConfig.cpp:245 +msgid "Compatible printers condition" +msgstr "Condição de impressoras compatíveis" + +#: src/libslic3r/PrintConfig.cpp:246 +msgid "" +"A boolean expression using the configuration values of an active printer " +"profile. If this expression evaluates to true, this profile is considered " +"compatible with the active printer profile." +msgstr "" +"Uma expressão booleana usando os valores de config. de um perfil de " +"impressora ativo. Se essa expressão for avaliada como verdadeira, esse " +"perfil será considerado compatível com o perfil de impressora ativo." + +#: src/libslic3r/PrintConfig.cpp:260 +msgid "Compatible print profiles condition" +msgstr "Condição de perfis de impressão compatíveis" + +#: src/libslic3r/PrintConfig.cpp:261 +msgid "" +"A boolean expression using the configuration values of an active print " +"profile. If this expression evaluates to true, this profile is considered " +"compatible with the active print profile." +msgstr "" +"Uma expressão booleana usando os valores de config. de um perfil de " +"impressão ativo. Se essa expressão for avaliada como verdadeira, esse perfil " +"será considerado compatível com o perfil de impressão ativo." + +#: src/libslic3r/PrintConfig.cpp:278 +msgid "Complete individual objects" +msgstr "Complete objetos individuais" + +#: src/libslic3r/PrintConfig.cpp:279 +msgid "" +"When printing multiple objects or copies, this feature will complete each " +"object before moving onto next one (and starting it from its bottom layer). " +"This feature is useful to avoid the risk of ruined prints. Slic3r should " +"warn and prevent you from extruder collisions, but beware." +msgstr "" +"Ao imprimir vários objetos ou cópias, esse recurso concluirá cada objeto " +"antes de passar para o próximo (e iniciando-o de sua camada inferior). Este " +"recurso é útil para evitar o risco de impressões arruinadas. Slic3r deve " +"avisar e impedi-lo de colisões de extrusoras, mas cuidado." + +#: src/libslic3r/PrintConfig.cpp:287 +msgid "Enable auto cooling" +msgstr "Ativar o resfriamento automático" + +#: src/libslic3r/PrintConfig.cpp:288 +msgid "" +"This flag enables the automatic cooling logic that adjusts print speed and " +"fan speed according to layer printing time." +msgstr "" +"Esse sinalizador permite a lógica de resfriamento automática que ajusta a " +"velocidade de impressão e a velocidade do ventoinha de acordo com o tempo de " +"impressão da camada." + +#: src/libslic3r/PrintConfig.cpp:293 +msgid "Cooling tube position" +msgstr "Posição do tubo de resfriamento" + +#: src/libslic3r/PrintConfig.cpp:294 +msgid "Distance of the center-point of the cooling tube from the extruder tip." +msgstr "" +"Distância do ponto central do tubo de resfriamento da ponta da extrusora." + +#: src/libslic3r/PrintConfig.cpp:301 +msgid "Cooling tube length" +msgstr "Comprimento do tubo de resfriamento" + +#: src/libslic3r/PrintConfig.cpp:302 +msgid "Length of the cooling tube to limit space for cooling moves inside it." +msgstr "" +"Comprimento do tubo de resfriamento para limitar o espaço para movimentos de " +"resfriamento dentro dele." + +#: src/libslic3r/PrintConfig.cpp:310 +msgid "" +"This is the acceleration your printer will be reset to after the role-" +"specific acceleration values are used (perimeter/infill). Set zero to " +"prevent resetting acceleration at all." +msgstr "" +"Esta é a aceleração que sua impressora será redefinida para depois que os " +"valores de aceleração específicos da função forem usados (perímetro/" +"preenchimento). Defina zero para evitar redefinir a aceleração em tudo." + +#: src/libslic3r/PrintConfig.cpp:319 +msgid "Default filament profile" +msgstr "Perfil de filamento padrão" + +#: src/libslic3r/PrintConfig.cpp:320 +msgid "" +"Default filament profile associated with the current printer profile. On " +"selection of the current printer profile, this filament profile will be " +"activated." +msgstr "" +"Perfil de filamento padrão associado ao perfil de impressora atual. Na " +"seleção do perfil da impressora atual, este perfil de filamento será ativado." + +#: src/libslic3r/PrintConfig.cpp:326 +msgid "Default print profile" +msgstr "Perfil de impressão padrão" + +#: src/libslic3r/PrintConfig.cpp:327 src/libslic3r/PrintConfig.cpp:2479 +#: src/libslic3r/PrintConfig.cpp:2490 +msgid "" +"Default print profile associated with the current printer profile. On " +"selection of the current printer profile, this print profile will be " +"activated." +msgstr "" +"Perfil de impressão padrão associado ao perfil de impressora atual. Na " +"seleção do perfil de impressora atual, este perfil de impressão será ativado." + +#: src/libslic3r/PrintConfig.cpp:333 +msgid "Disable fan for the first" +msgstr "Desabilite o ventoinha para a(s) primeira(s)" + +#: src/libslic3r/PrintConfig.cpp:334 +msgid "" +"You can set this to a positive value to disable fan at all during the first " +"layers, so that it does not make adhesion worse." +msgstr "" +"Você pode ajustar isto a um valor positivo para desabilitar a ventoinha " +"durante as primeiras camadas, de modo que melhore a adesão." + +#: src/libslic3r/PrintConfig.cpp:336 src/libslic3r/PrintConfig.cpp:971 +#: src/libslic3r/PrintConfig.cpp:1484 src/libslic3r/PrintConfig.cpp:1669 +#: src/libslic3r/PrintConfig.cpp:1730 src/libslic3r/PrintConfig.cpp:1894 +#: src/libslic3r/PrintConfig.cpp:1939 +msgid "layers" +msgstr "camadas" + +#: src/libslic3r/PrintConfig.cpp:343 +msgid "Don't support bridges" +msgstr "Não suporte pontes" + +#: src/libslic3r/PrintConfig.cpp:345 +msgid "" +"Experimental option for preventing support material from being generated " +"under bridged areas." +msgstr "" +"Opção experimental para impedir que o material de suporte seja gerado em " +"áreas com ponte." + +#: src/libslic3r/PrintConfig.cpp:351 +msgid "Distance between copies" +msgstr "Distância entre cópias" + +#: src/libslic3r/PrintConfig.cpp:352 +msgid "Distance used for the auto-arrange feature of the plater." +msgstr "Distância usada para o recurso de organizar automaticamente o prato." + +#: src/libslic3r/PrintConfig.cpp:359 +msgid "Elephant foot compensation" +msgstr "Compensação do pé do elefante" + +#: src/libslic3r/PrintConfig.cpp:361 +msgid "" +"The first layer will be shrunk in the XY plane by the configured value to " +"compensate for the 1st layer squish aka an Elephant Foot effect." +msgstr "" +"A primeira camada será encolhido no plano XY pelo valor config.urado para " +"compensar a 1ª camada esmagada, também conhecida como pé de elefante." + +#: src/libslic3r/PrintConfig.cpp:370 +msgid "" +"This end procedure is inserted at the end of the output file. Note that you " +"can use placeholder variables for all PrusaSlicer settings." +msgstr "" +"Este procedimento final é inserido no final do arquivo de saída. Observe que " +"você pode usar variáveis de espaço reservado para todas as config. de " +"PrusaSlicer." + +#: src/libslic3r/PrintConfig.cpp:380 +msgid "" +"This end procedure is inserted at the end of the output file, before the " +"printer end gcode (and before any toolchange from this filament in case of " +"multimaterial printers). Note that you can use placeholder variables for all " +"PrusaSlicer settings. If you have multiple extruders, the gcode is processed " +"in extruder order." +msgstr "" +"Este procedimento final é inserido no final do arquivo de saída, antes da " +"extremidade da impressora Gcode (e antes de qualquer troca de ferramenta " +"deste filamento em caso de impressoras multimaterial). Observe que você pode " +"usar variáveis de espaço reservado para todas as config. de PrusaSlicer. Se " +"você tiver várias extrusoras, o Gcode é processado em ordem de extrusora." + +#: src/libslic3r/PrintConfig.cpp:391 +msgid "Ensure vertical shell thickness" +msgstr "Assegure a espessura vertical da parede" + +#: src/libslic3r/PrintConfig.cpp:393 +msgid "" +"Add solid infill near sloping surfaces to guarantee the vertical shell " +"thickness (top+bottom solid layers)." +msgstr "" +"Adicionar preenchimento sólido perto de superfícies inclinadas para garantir " +"a espessura do escudo vertical (camadas sólidas no topo + base )." + +#: src/libslic3r/PrintConfig.cpp:399 +msgid "Top fill pattern" +msgstr "Padrão de preenchimento do topo" + +#: src/libslic3r/PrintConfig.cpp:401 +msgid "" +"Fill pattern for top infill. This only affects the top visible layer, and " +"not its adjacent solid shells." +msgstr "" +"Padrão de preenchimento para preenchimento do topo. Isto afeta somente a " +"camada visível superior, e não suas paredes adjacentes." + +#: src/libslic3r/PrintConfig.cpp:409 src/libslic3r/PrintConfig.cpp:821 +#: src/libslic3r/PrintConfig.cpp:1972 +msgid "Rectilinear" +msgstr "Rectilíneo" + +#: src/libslic3r/PrintConfig.cpp:410 src/libslic3r/PrintConfig.cpp:827 +msgid "Concentric" +msgstr "Concêntrico" + +#: src/libslic3r/PrintConfig.cpp:411 src/libslic3r/PrintConfig.cpp:831 +msgid "Hilbert Curve" +msgstr "Curva de Hilbert" + +#: src/libslic3r/PrintConfig.cpp:412 src/libslic3r/PrintConfig.cpp:832 +msgid "Archimedean Chords" +msgstr "Cordas Archimedean" + +#: src/libslic3r/PrintConfig.cpp:413 src/libslic3r/PrintConfig.cpp:833 +msgid "Octagram Spiral" +msgstr "Espiral estrelado" + +#: src/libslic3r/PrintConfig.cpp:419 +msgid "Bottom fill pattern" +msgstr "Padrão de preenchimento da base" + +#: src/libslic3r/PrintConfig.cpp:421 +msgid "" +"Fill pattern for bottom infill. This only affects the bottom external " +"visible layer, and not its adjacent solid shells." +msgstr "" +"Padrão de preenchimento para preenchimento da base. Isto afeta somente a " +"camada visível externa inferior, e não suas paredes adjacentes." + +#: src/libslic3r/PrintConfig.cpp:430 src/libslic3r/PrintConfig.cpp:440 +msgid "External perimeters" +msgstr "Perímetros externos" + +#: src/libslic3r/PrintConfig.cpp:432 +msgid "" +"Set this to a non-zero value to set a manual extrusion width for external " +"perimeters. If left zero, default extrusion width will be used if set, " +"otherwise 1.125 x nozzle diameter will be used. If expressed as percentage " +"(for example 200%), it will be computed over layer height." +msgstr "" +"Defina isso como um valor diferente de zero para definir uma largura de " +"extrusão manual para perímetros externos. Se for deixado zero, a largura de " +"extrusão padrão será usada se definido, caso contrário, 1,125 x diâmetro da " +"ponteira será usado. Se expresso em porcentagem(por exemplo 200%), será " +"calculado sobre a altura da camada." + +#: src/libslic3r/PrintConfig.cpp:435 src/libslic3r/PrintConfig.cpp:543 +#: src/libslic3r/PrintConfig.cpp:860 src/libslic3r/PrintConfig.cpp:872 +#: src/libslic3r/PrintConfig.cpp:992 src/libslic3r/PrintConfig.cpp:1017 +#: src/libslic3r/PrintConfig.cpp:1403 src/libslic3r/PrintConfig.cpp:1741 +#: src/libslic3r/PrintConfig.cpp:1847 src/libslic3r/PrintConfig.cpp:1915 +#: src/libslic3r/PrintConfig.cpp:2074 +msgid "mm or %" +msgstr "mm ou %" + +#: src/libslic3r/PrintConfig.cpp:442 +msgid "" +"This separate setting will affect the speed of external perimeters (the " +"visible ones). If expressed as percentage (for example: 80%) it will be " +"calculated on the perimeters speed setting above. Set to zero for auto." +msgstr "" +"Esta config. separada afetará a velocidade dos perímetros externos (os " +"visíveis). Se expresso em porcentagem(por exemplo: 80%) Ele será calculado " +"sobre a velocidade de perímetros config. acima. Defina como zero para auto." + +#: src/libslic3r/PrintConfig.cpp:445 src/libslic3r/PrintConfig.cpp:881 +#: src/libslic3r/PrintConfig.cpp:1700 src/libslic3r/PrintConfig.cpp:1751 +#: src/libslic3r/PrintConfig.cpp:1958 src/libslic3r/PrintConfig.cpp:2086 +msgid "mm/s or %" +msgstr "mm/s ou %" + +#: src/libslic3r/PrintConfig.cpp:452 +msgid "External perimeters first" +msgstr "Perímetros externos primeiro" + +#: src/libslic3r/PrintConfig.cpp:454 +msgid "" +"Print contour perimeters from the outermost one to the innermost one instead " +"of the default inverse order." +msgstr "" +"Imprima perímetros de contorno do mais externo para o mais interno em vez da " +"ordem inversa padrão." + +#: src/libslic3r/PrintConfig.cpp:460 +msgid "Extra perimeters if needed" +msgstr "Perímetros extras se necessário" + +#: src/libslic3r/PrintConfig.cpp:462 +#, c-format +msgid "" +"Add more perimeters when needed for avoiding gaps in sloping walls. Slic3r " +"keeps adding perimeters, until more than 70% of the loop immediately above " +"is supported." +msgstr "" +"Adicione mais perímetros quando necessário para evitar lacunas em paredes " +"inclinados. Slic3r continua adicionando perímetros, até que mais de 70% o do " +"loop imediatamente acima é suportado." + +#: src/libslic3r/PrintConfig.cpp:472 +msgid "" +"The extruder to use (unless more specific extruder settings are specified). " +"This value overrides perimeter and infill extruders, but not the support " +"extruders." +msgstr "" +"A extrusora a ser usada (a menos que config. de extrusoras mais específicas " +"sejam especificadas). Esse valor substitui as extrusoras de perímetro e " +"preenchimento, mas não as extrusoras de suporte." + +#: src/libslic3r/PrintConfig.cpp:484 +msgid "" +"Set this to the vertical distance between your nozzle tip and (usually) the " +"X carriage rods. In other words, this is the height of the clearance " +"cylinder around your extruder, and it represents the maximum depth the " +"extruder can peek before colliding with other printed objects." +msgstr "" +"Defina isto para a distância vertical entre a ponta do bico e (normalmente) " +"as hastes do X. Em outras palavras, esta é a altura do cilindro de folga em " +"torno de sua extrusora, e representa a profundidade máxima que a extrusora " +"pode espreitar antes de colidir com outros objetos impressos." + +#: src/libslic3r/PrintConfig.cpp:494 +msgid "Radius" +msgstr "Raio" + +#: src/libslic3r/PrintConfig.cpp:495 +msgid "" +"Set this to the clearance radius around your extruder. If the extruder is " +"not centered, choose the largest value for safety. This setting is used to " +"check for collisions and to display the graphical preview in the plater." +msgstr "" +"Defina isso para o raio de folga em torno de sua extrusora. Se a extrusora " +"não estiver centralizada, escolha o maior valor para a segurança. Essa " +"config. é usada para verificar colisões e exibir a visualização gráfica no " +"prato." + +#: src/libslic3r/PrintConfig.cpp:505 +msgid "Extruder Color" +msgstr "Cor da extrusora" + +#: src/libslic3r/PrintConfig.cpp:506 src/libslic3r/PrintConfig.cpp:566 +msgid "This is only used in the Slic3r interface as a visual help." +msgstr "Isso é usado apenas na interface Slic3r como uma ajuda visual." + +#: src/libslic3r/PrintConfig.cpp:512 +msgid "Extruder offset" +msgstr "Compensamento da extrusora" + +#: src/libslic3r/PrintConfig.cpp:513 +msgid "" +"If your firmware doesn't handle the extruder displacement you need the G-" +"code to take it into account. This option lets you specify the displacement " +"of each extruder with respect to the first one. It expects positive " +"coordinates (they will be subtracted from the XY coordinate)." +msgstr "" +"Se o seu firmware não manipula o deslocamento da extrusora, você precisa do " +"G-code para levá-lo em conta. Esta opção permite especificar o deslocamento " +"de cada extrusora em relação à primeira. Ele espera coordenadas positivas " +"(eles serão subtraída da coordenada XY)." + +#: src/libslic3r/PrintConfig.cpp:522 +msgid "Extrusion axis" +msgstr "Eixo de extrusão" + +#: src/libslic3r/PrintConfig.cpp:523 +msgid "" +"Use this option to set the axis letter associated to your printer's extruder " +"(usually E but some printers use A)." +msgstr "" +"Use esta opção para definir a letra do eixo associada à extrusora da sua " +"impressora (geralmente E, mas algumas impressoras usam A)." + +#: src/libslic3r/PrintConfig.cpp:528 +msgid "Extrusion multiplier" +msgstr "Multiplicador de extrusão" + +#: src/libslic3r/PrintConfig.cpp:529 +msgid "" +"This factor changes the amount of flow proportionally. You may need to tweak " +"this setting to get nice surface finish and correct single wall widths. " +"Usual values are between 0.9 and 1.1. If you think you need to change this " +"more, check filament diameter and your firmware E steps." +msgstr "" +"Esse fator altera a quantidade de fluxo proporcionalmente. Você pode " +"precisar de ajustar esta config. para obter acabamento de superfície " +"agradável e corrigir larguras de parede única. Os valores usuais são entre " +"0,9 e 1,1. Se você acha que precisa mudar isso mais, verifique o diâmetro do " +"filamento e os passos configurados no firmware da extrusora." + +#: src/libslic3r/PrintConfig.cpp:537 +msgid "Default extrusion width" +msgstr "Largura de extrusão padrão" + +#: src/libslic3r/PrintConfig.cpp:539 +msgid "" +"Set this to a non-zero value to allow a manual extrusion width. If left to " +"zero, Slic3r derives extrusion widths from the nozzle diameter (see the " +"tooltips for perimeter extrusion width, infill extrusion width etc). If " +"expressed as percentage (for example: 230%), it will be computed over layer " +"height." +msgstr "" +"Defina isso como um valor diferente de zero para permitir uma largura de " +"extrusão manual. Se deixado a zero, Slic3r deriva larguras da extrusão do " +"diâmetro da ponteira (veja as dicas ferramentas para a largura da extrusão " +"do perímetro, a largura de extrusão do preenchimento etc.). Se expresso como " +"porcentagem (por exemplo: 230%), ele será calculado sobre a altura da camada." + +#: src/libslic3r/PrintConfig.cpp:548 +msgid "Keep fan always on" +msgstr "Mantenha a ventoinha sempre ligada" + +#: src/libslic3r/PrintConfig.cpp:549 +msgid "" +"If this is enabled, fan will never be disabled and will be kept running at " +"least at its minimum speed. Useful for PLA, harmful for ABS." +msgstr "" +"Se isso estiver ativado, a ventoinha nunca será desativada e será mantida " +"funcionando pelo menos em sua velocidade mínima. Útil para o PLA, " +"prejudicial para o ABS." + +#: src/libslic3r/PrintConfig.cpp:554 +msgid "Enable fan if layer print time is below" +msgstr "Ative o ventoinha se o tempo de impressão da camada estiver abaixo" + +#: src/libslic3r/PrintConfig.cpp:555 +msgid "" +"If layer print time is estimated below this number of seconds, fan will be " +"enabled and its speed will be calculated by interpolating the minimum and " +"maximum speeds." +msgstr "" +"Se o tempo de impressão da camada for estimado abaixo desse número de " +"segundos, a ventoinha será ativada e sua velocidade será calculada " +"interpolando as velocidades mínima e máxima." + +#: src/libslic3r/PrintConfig.cpp:557 src/libslic3r/PrintConfig.cpp:1687 +msgid "approximate seconds" +msgstr "segundos aproximados" + +#: src/libslic3r/PrintConfig.cpp:571 +msgid "Filament notes" +msgstr "Notas de filamento" + +#: src/libslic3r/PrintConfig.cpp:572 +msgid "You can put your notes regarding the filament here." +msgstr "Você pode colocar suas anotações sobre o filamento aqui." + +#: src/libslic3r/PrintConfig.cpp:580 src/libslic3r/PrintConfig.cpp:1231 +msgid "Max volumetric speed" +msgstr "Máxima velocidade volumétrica" + +#: src/libslic3r/PrintConfig.cpp:581 +msgid "" +"Maximum volumetric speed allowed for this filament. Limits the maximum " +"volumetric speed of a print to the minimum of print and filament volumetric " +"speed. Set to zero for no limit." +msgstr "" +"Velocidade máxima volumétrica permitida para este filamento. Limita a " +"velocidade volumétrica máxima de uma impressão ao mínimo de velocidade " +"volumétrica de impressão e de filamento. Defina como zero para nenhum limite." + +#: src/libslic3r/PrintConfig.cpp:590 +msgid "Loading speed" +msgstr "Velocidade de carregamento" + +#: src/libslic3r/PrintConfig.cpp:591 +msgid "Speed used for loading the filament on the wipe tower." +msgstr "Velocidade utilizada para carregar o filamento na torre de limpeza." + +#: src/libslic3r/PrintConfig.cpp:598 +msgid "Loading speed at the start" +msgstr "Velocidade de carregamento no início" + +#: src/libslic3r/PrintConfig.cpp:599 +msgid "Speed used at the very beginning of loading phase." +msgstr "Velocidade utilizada no início da fase de carregamento." + +#: src/libslic3r/PrintConfig.cpp:606 +msgid "Unloading speed" +msgstr "Velocidade de descarregamento" + +#: src/libslic3r/PrintConfig.cpp:607 +msgid "" +"Speed used for unloading the filament on the wipe tower (does not affect " +"initial part of unloading just after ramming)." +msgstr "" +"Velocidade utilizada para descarregar o filamento na torre de limpeza (não " +"afeta a parte inicial do descarregamento logo após o Ramming)." + +#: src/libslic3r/PrintConfig.cpp:615 +msgid "Unloading speed at the start" +msgstr "Velocidade de descarregamento no início" + +#: src/libslic3r/PrintConfig.cpp:616 +msgid "" +"Speed used for unloading the tip of the filament immediately after ramming." +msgstr "" +"Velocidade usada para descarregar a ponta do filamento imediatamente após o " +"Ramming." + +#: src/libslic3r/PrintConfig.cpp:623 +msgid "Delay after unloading" +msgstr "Atraso após o descarregamento" + +#: src/libslic3r/PrintConfig.cpp:624 +msgid "" +"Time to wait after the filament is unloaded. May help to get reliable " +"toolchanges with flexible materials that may need more time to shrink to " +"original dimensions." +msgstr "" +"Tempo de espera após o filamento ser descarregado. Pode ajudar a obter " +"trocas de ferramenta confiáveis com materiais flexíveis que podem precisar " +"de mais tempo para reduzir as dimensões originais." + +#: src/libslic3r/PrintConfig.cpp:633 +msgid "Number of cooling moves" +msgstr "Número de movimentos de resfriamento" + +#: src/libslic3r/PrintConfig.cpp:634 +msgid "" +"Filament is cooled by being moved back and forth in the cooling tubes. " +"Specify desired number of these moves." +msgstr "" +"O filamento é resfriado por ser movido para frente e para trás nos tubos de " +"resfriamento. Especifique o número desejado desses movimentos." + +#: src/libslic3r/PrintConfig.cpp:642 +msgid "Speed of the first cooling move" +msgstr "Velocidade do primeiro movimento de resfriamento" + +#: src/libslic3r/PrintConfig.cpp:643 +msgid "Cooling moves are gradually accelerating beginning at this speed." +msgstr "" +"Movimentos de resfriamento estão gradualmente acelerando a partir desta " +"velocidade." + +#: src/libslic3r/PrintConfig.cpp:650 +msgid "Minimal purge on wipe tower" +msgstr "Remoção mínima na torre da limpeza" + +#: src/libslic3r/PrintConfig.cpp:651 +msgid "" +"After a tool change, the exact position of the newly loaded filament inside " +"the nozzle may not be known, and the filament pressure is likely not yet " +"stable. Before purging the print head into an infill or a sacrificial " +"object, Slic3r will always prime this amount of material into the wipe tower " +"to produce successive infill or sacrificial object extrusions reliably." +msgstr "" +"Após uma mudança da ferramenta, a posição exata do filamento recentemente " +"carregado dentro da ponteira pode não ser conhecida, e a pressão do " +"filamento provavelmente ainda não esteja estável. Antes de purgar a cabeça " +"de impressão em um preenchimento ou um objeto sacrificial, Slic3r sempre " +"Prime esta quantidade de material para a torre de limpeza para produzir " +"sucessivas preenchimento ou sacrificial objeto extrusões de forma confiável." + +#: src/libslic3r/PrintConfig.cpp:655 +msgid "mm³" +msgstr "mm³" + +#: src/libslic3r/PrintConfig.cpp:661 +msgid "Speed of the last cooling move" +msgstr "Velocidade do último movimento de resfriamento" + +#: src/libslic3r/PrintConfig.cpp:662 +msgid "Cooling moves are gradually accelerating towards this speed." +msgstr "" +"Movimentos de resfriamento estão gradualmente acelerando para esta " +"velocidade." + +#: src/libslic3r/PrintConfig.cpp:669 +msgid "Filament load time" +msgstr "Tempo de carga do filamento" + +#: src/libslic3r/PrintConfig.cpp:670 +msgid "" +"Time for the printer firmware (or the Multi Material Unit 2.0) to load a new " +"filament during a tool change (when executing the T code). This time is " +"added to the total print time by the G-code time estimator." +msgstr "" +"Tempo para o firmware da impressora (ou a Multi Material Unit 2.0 para " +"carregar um novo filamento durante uma mudança de ferramenta (ao executar o " +"código T). Esse tempo é adicionado ao tempo total de impressão pelo " +"estimador de tempo do G-code." + +#: src/libslic3r/PrintConfig.cpp:677 +msgid "Ramming parameters" +msgstr "Parâmetros de Ramming" + +#: src/libslic3r/PrintConfig.cpp:678 +msgid "" +"This string is edited by RammingDialog and contains ramming specific " +"parameters." +msgstr "" +"Essa cadeia de caracteres é editada por rammingdialog e contém parâmetros " +"específicos de Ramming." + +#: src/libslic3r/PrintConfig.cpp:684 +msgid "Filament unload time" +msgstr "Tempo de descarregamento do filamento" + +#: src/libslic3r/PrintConfig.cpp:685 +msgid "" +"Time for the printer firmware (or the Multi Material Unit 2.0) to unload a " +"filament during a tool change (when executing the T code). This time is " +"added to the total print time by the G-code time estimator." +msgstr "" +"Tempo para o firmware da impressora (ou a unidade de material multi 2,0) " +"para descarregar um filamento durante uma mudança de ferramenta (ao executar " +"o código T). Esse tempo é adicionado ao tempo total de impressão pelo " +"estimador de tempo do G-code." + +#: src/libslic3r/PrintConfig.cpp:693 +msgid "" +"Enter your filament diameter here. Good precision is required, so use a " +"caliper and do multiple measurements along the filament, then compute the " +"average." +msgstr "" +"Insira o diâmetro do filamento aqui. Boa precisão é necessária, então use um " +"paquímetro e fazer várias medições ao longo do filamento, em seguida, " +"calcular a média." + +#: src/libslic3r/PrintConfig.cpp:700 +msgid "Density" +msgstr "Densidade" + +#: src/libslic3r/PrintConfig.cpp:701 +msgid "" +"Enter your filament density here. This is only for statistical information. " +"A decent way is to weigh a known length of filament and compute the ratio of " +"the length to volume. Better is to calculate the volume directly through " +"displacement." +msgstr "" +"Insira sua densidade de filamento aqui. Isto é apenas para informação " +"estatística. Uma maneira decente é pesar um comprimento conhecido do " +"filamento e computar a relação do comprimento ao volume. Melhor é calcular o " +"volume diretamente através do deslocamento." + +#: src/libslic3r/PrintConfig.cpp:704 +msgid "g/cm³" +msgstr "g/cm³" + +#: src/libslic3r/PrintConfig.cpp:709 +msgid "Filament type" +msgstr "Tipo de filamento" + +#: src/libslic3r/PrintConfig.cpp:710 +msgid "The filament material type for use in custom G-codes." +msgstr "O tipo de material de filamento para uso em G-code customizados." + +#: src/libslic3r/PrintConfig.cpp:736 +msgid "Soluble material" +msgstr "Material solúvel" + +#: src/libslic3r/PrintConfig.cpp:737 +msgid "Soluble material is most likely used for a soluble support." +msgstr "O material solúvel é mais provável usado para um suporte solúvel." + +#: src/libslic3r/PrintConfig.cpp:743 +msgid "" +"Enter your filament cost per kg here. This is only for statistical " +"information." +msgstr "" +"Insira o seu custo de filamento por kg aqui. Isto é apenas para informação " +"estatística." + +#: src/libslic3r/PrintConfig.cpp:744 +msgid "money/kg" +msgstr "dinheiro/kg" + +#: src/libslic3r/PrintConfig.cpp:753 +msgid "Fill angle" +msgstr "Ângulo de preenchimento" + +#: src/libslic3r/PrintConfig.cpp:755 +msgid "" +"Default base angle for infill orientation. Cross-hatching will be applied to " +"this. Bridges will be infilled using the best direction Slic3r can detect, " +"so this setting does not affect them." +msgstr "" +"Ângulo padrão para a orientação de preenchimento. A hachura cruzada será " +"aplicada a isso. Pontes serão preenchidas usando a melhor direção Slic3r " +"pode detectar, portanto, essa config. não vai afeta-los." + +#: src/libslic3r/PrintConfig.cpp:767 +msgid "Fill density" +msgstr "Densidade de preenchimento" + +#: src/libslic3r/PrintConfig.cpp:769 +msgid "Density of internal infill, expressed in the range 0% - 100%." +msgstr "Densidade de preenchimento interno, expresso na faixa de 0%-100%." + +#: src/libslic3r/PrintConfig.cpp:804 +msgid "Fill pattern" +msgstr "Padrão de preenchimento" + +#: src/libslic3r/PrintConfig.cpp:806 +msgid "Fill pattern for general low-density infill." +msgstr "Padrão de preenchimento para preenchimento de baixa densidade." + +#: src/libslic3r/PrintConfig.cpp:822 +msgid "Grid" +msgstr "Grade" + +#: src/libslic3r/PrintConfig.cpp:823 +msgid "Triangles" +msgstr "Triângulos" + +#: src/libslic3r/PrintConfig.cpp:824 +msgid "Stars" +msgstr "Estrelas" + +#: src/libslic3r/PrintConfig.cpp:825 +msgid "Cubic" +msgstr "Cúbico" + +#: src/libslic3r/PrintConfig.cpp:826 +msgid "Line" +msgstr "Linha" + +#: src/libslic3r/PrintConfig.cpp:828 src/libslic3r/PrintConfig.cpp:1974 +msgid "Honeycomb" +msgstr "Hexágono" + +#: src/libslic3r/PrintConfig.cpp:829 +msgid "3D Honeycomb" +msgstr "Hexágono 3D" + +#: src/libslic3r/PrintConfig.cpp:830 +msgid "Gyroid" +msgstr "Giróide" + +#: src/libslic3r/PrintConfig.cpp:837 src/libslic3r/PrintConfig.cpp:846 +#: src/libslic3r/PrintConfig.cpp:854 src/libslic3r/PrintConfig.cpp:887 +msgid "First layer" +msgstr "Primeira camada" + +#: src/libslic3r/PrintConfig.cpp:838 +msgid "" +"This is the acceleration your printer will use for first layer. Set zero to " +"disable acceleration control for first layer." +msgstr "" +"Esta é a aceleração que sua impressora usará para a primeira camada. Defina " +"zero para desabilitar o controle de aceleração para a primeira camada." + +#: src/libslic3r/PrintConfig.cpp:847 +msgid "" +"Heated build plate temperature for the first layer. Set this to zero to " +"disable bed temperature control commands in the output." +msgstr "" +"Temperatura da mesa aquecida para a primeira camada. Defina isso como zero " +"para desabilitar os comandos de controle de temperatura da mesa na saída." + +#: src/libslic3r/PrintConfig.cpp:856 +msgid "" +"Set this to a non-zero value to set a manual extrusion width for first " +"layer. You can use this to force fatter extrudates for better adhesion. If " +"expressed as percentage (for example 120%) it will be computed over first " +"layer height. If set to zero, it will use the default extrusion width." +msgstr "" +"Defina isso como um valor diferente de zero para definir uma largura de " +"extrusão manual para a primeira camada. Você pode usar este para forçar " +"extrusões maiores para a melhor adesão. Se expresso em porcentagem(por " +"exemplo, 120%) será computado sobre a primeira altura da camada. Se definido " +"como zero, ele usará a largura de extrusão padrão." + +#: src/libslic3r/PrintConfig.cpp:866 +msgid "First layer height" +msgstr "Altura da primeira camada" + +#: src/libslic3r/PrintConfig.cpp:868 +msgid "" +"When printing with very low layer heights, you might still want to print a " +"thicker bottom layer to improve adhesion and tolerance for non perfect build " +"plates. This can be expressed as an absolute value or as a percentage (for " +"example: 150%) over the default layer height." +msgstr "" +"Ao imprimir com alturas muito baixas da camada, você pode ainda querer " +"imprimir uma camada inferior mais grossa para melhorar a adesão e a " +"tolerância para mesas não perfeitas. Isso pode ser expresso como um valor " +"absoluto ou como uma porcentagem (por exemplo: 150%) sobre a altura da " +"camada padrão." + +#: src/libslic3r/PrintConfig.cpp:877 +msgid "First layer speed" +msgstr "Velocidade da primeira camada" + +#: src/libslic3r/PrintConfig.cpp:878 +msgid "" +"If expressed as absolute value in mm/s, this speed will be applied to all " +"the print moves of the first layer, regardless of their type. If expressed " +"as a percentage (for example: 40%) it will scale the default speeds." +msgstr "" +"Se expresso como valor absoluto em mm/s, esta velocidade será aplicada a " +"todos os movimentos de impressão da primeira camada, independentemente do " +"seu tipo. Se expresso em porcentagem(por exemplo: 40%) Ele dimensionará as " +"velocidades padrão." + +#: src/libslic3r/PrintConfig.cpp:888 +msgid "" +"Extruder temperature for first layer. If you want to control temperature " +"manually during print, set this to zero to disable temperature control " +"commands in the output file." +msgstr "" +"Temperatura da extrusora para a primeira camada. Se você quiser controlar a " +"temperatura manualmente durante a impressão, defina isso como zero para " +"desabilitar os comandos de controle de temperatura no arquivo de saída." + +#: src/libslic3r/PrintConfig.cpp:897 +msgid "" +"Speed for filling small gaps using short zigzag moves. Keep this reasonably " +"low to avoid too much shaking and resonance issues. Set zero to disable gaps " +"filling." +msgstr "" +"Velocidade para encher pequenas lacunas usando movimentos de ziguezague " +"curtos. Mantenha este razoavelmente baixo para evitar demasiada agitação e " +"problemas de ressonância. Defina zero para desabilitar o preenchimento de " +"lacunas." + +#: src/libslic3r/PrintConfig.cpp:905 +msgid "Verbose G-code" +msgstr "Gcode detalhado" + +#: src/libslic3r/PrintConfig.cpp:906 +msgid "" +"Enable this to get a commented G-code file, with each line explained by a " +"descriptive text. If you print from SD card, the additional weight of the " +"file could make your firmware slow down." +msgstr "" +"Habilite isso para obter um arquivo de G-code comentado, com cada linha " +"explicada por um texto descritivo. Se você imprimir a partir do cartão SD, o " +"peso adicional do arquivo pode fazer o seu firmware ficar mais lento." + +#: src/libslic3r/PrintConfig.cpp:913 +msgid "G-code flavor" +msgstr "Tipo de G-code" + +#: src/libslic3r/PrintConfig.cpp:914 +msgid "" +"Some G/M-code commands, including temperature control and others, are not " +"universal. Set this option to your printer's firmware to get a compatible " +"output. The \"No extrusion\" flavor prevents PrusaSlicer from exporting any " +"extrusion value at all." +msgstr "" +"Alguns comandos G/M-code, incluindo controle de temperatura e outros, não " +"são universais. Defina esta opção para o firmware da impressora para obter " +"uma saída compatível. O \"sem extrusão\" tipo impede PrusaSlicer de exportar " +"qualquer valor de extrusão em tudo." + +#: src/libslic3r/PrintConfig.cpp:937 +msgid "No extrusion" +msgstr "Sem extrusão" + +#: src/libslic3r/PrintConfig.cpp:942 +msgid "Label objects" +msgstr "Rotular objetos" + +#: src/libslic3r/PrintConfig.cpp:943 +msgid "" +"Enable this to add comments into the G-Code labeling print moves with what " +"object they belong to, which is useful for the Octoprint CancelObject " +"plugin. This settings is NOT compatible with Single Extruder Multi Material " +"setup and Wipe into Object / Wipe into Infill." +msgstr "" +"Habilite isso para adicionar comentários aos movimentos de impressão de " +"rotulagem do G-code com o objeto ao qual eles pertencem, o que é útil para o " +"plugin Octoprint CancelObject. Essas config. não são compatíveis com a " +"config. de multi material de extrusora única e limpe em objeto/limpar em " +"preenchimento." + +#: src/libslic3r/PrintConfig.cpp:950 +msgid "High extruder current on filament swap" +msgstr "Corrente elevada da extrusora na troca do filamento" + +#: src/libslic3r/PrintConfig.cpp:951 +msgid "" +"It may be beneficial to increase the extruder motor current during the " +"filament exchange sequence to allow for rapid ramming feed rates and to " +"overcome resistance when loading a filament with an ugly shaped tip." +msgstr "" +"Pode ser benéfico aumentar a corrente do motor da extrusora durante a " +"seqüência da troca do filamento para permitir taxas de alimentação de " +"Ramming rápidas e para superar a resistência ao carregar um filamento com " +"uma ponta feia." + +#: src/libslic3r/PrintConfig.cpp:959 +msgid "" +"This is the acceleration your printer will use for infill. Set zero to " +"disable acceleration control for infill." +msgstr "" +"Esta é a aceleração que sua impressora usará para preenchimento. Defina zero " +"para desabilitar o controle de aceleração para preenchimento." + +#: src/libslic3r/PrintConfig.cpp:967 +msgid "Combine infill every" +msgstr "Combine preenchimento a cada" + +#: src/libslic3r/PrintConfig.cpp:969 +msgid "" +"This feature allows to combine infill and speed up your print by extruding " +"thicker infill layers while preserving thin perimeters, thus accuracy." +msgstr "" +"Este recurso permite combinar preenchimento e acelerar a sua impressão por " +"extrusão camadas de preenchimento mais espessa, preservando perímetros " +"finos, assim, a precisão." + +#: src/libslic3r/PrintConfig.cpp:972 +msgid "Combine infill every n layers" +msgstr "Combine preenchimento cada n camadas" + +#: src/libslic3r/PrintConfig.cpp:978 +msgid "Infill extruder" +msgstr "Extrusora de preenchimento" + +#: src/libslic3r/PrintConfig.cpp:980 +msgid "The extruder to use when printing infill." +msgstr "" +"A extrusora a ser utilizada quando estiver imprimindo preenchimento sólido." + +#: src/libslic3r/PrintConfig.cpp:988 +msgid "" +"Set this to a non-zero value to set a manual extrusion width for infill. If " +"left zero, default extrusion width will be used if set, otherwise 1.125 x " +"nozzle diameter will be used. You may want to use fatter extrudates to speed " +"up the infill and make your parts stronger. If expressed as percentage (for " +"example 90%) it will be computed over layer height." +msgstr "" +"Defina isso como um valor diferente de zero para definir uma largura de " +"extrusão manual para preenchimento. Se for deixado zero, a largura de " +"extrusão padrão será usada se definido, caso contrário, 1,125 x diâmetro da " +"ponteira será usado. Você pode querer usar extrusora mais larga para " +"acelerar o preenchimento e tornar suas peças mais fortes. Se expresso em " +"porcentagem(por exemplo, 90%) Ele será calculado sobre a altura da camada." + +#: src/libslic3r/PrintConfig.cpp:997 +msgid "Infill before perimeters" +msgstr "Preenchimento antes dos perímetros" + +#: src/libslic3r/PrintConfig.cpp:998 +msgid "" +"This option will switch the print order of perimeters and infill, making the " +"latter first." +msgstr "" +"Esta opção irá mudar a ordem de impressão de perímetros e preenchimento, " +"tornando o último primeiro." + +#: src/libslic3r/PrintConfig.cpp:1003 +msgid "Only infill where needed" +msgstr "Somente preenchimento onde necessário" + +#: src/libslic3r/PrintConfig.cpp:1005 +msgid "" +"This option will limit infill to the areas actually needed for supporting " +"ceilings (it will act as internal support material). If enabled, slows down " +"the G-code generation due to the multiple checks involved." +msgstr "" +"Esta opção limitará a preenchimento às áreas realmente necessárias para " +"suportar tetos (atuará como o material de sustentação interno). Se " +"habilitada, retarda a geração de G-code devido às várias verificações " +"envolvidas." + +#: src/libslic3r/PrintConfig.cpp:1012 +msgid "Infill/perimeters overlap" +msgstr "Sobreposição de preenchimento/perímetros" + +#: src/libslic3r/PrintConfig.cpp:1014 +msgid "" +"This setting applies an additional overlap between infill and perimeters for " +"better bonding. Theoretically this shouldn't be needed, but backlash might " +"cause gaps. If expressed as percentage (example: 15%) it is calculated over " +"perimeter extrusion width." +msgstr "" +"Esta config. aplica uma sobreposição adicional entre preenchimento e " +"perímetros para melhor colagem. Teoricamente isso não deveria ser " +"necessário, mas a folga pode causar lacunas. Se expresso em " +"porcentagem(exemplo: 15%) é calculado sobre a largura da extrusão do " +"perímetro." + +#: src/libslic3r/PrintConfig.cpp:1025 +msgid "Speed for printing the internal fill. Set to zero for auto." +msgstr "" +"Velocidade para imprimir o preenchimento interno. Defina como zero para auto." + +#: src/libslic3r/PrintConfig.cpp:1033 +msgid "Inherits profile" +msgstr "Herda o perfil" + +#: src/libslic3r/PrintConfig.cpp:1034 +msgid "Name of the profile, from which this profile inherits." +msgstr "Nome do perfil, a partir do qual este perfil herda." + +#: src/libslic3r/PrintConfig.cpp:1047 +msgid "Interface shells" +msgstr "Interface dos perímetros externos." + +#: src/libslic3r/PrintConfig.cpp:1048 +msgid "" +"Force the generation of solid shells between adjacent materials/volumes. " +"Useful for multi-extruder prints with translucent materials or manual " +"soluble support material." +msgstr "" +"Force a geração de perímetros externos sólidas entre materiais/volumes " +"adjacentes. Útil para cópias da multi-extrusora com materiais translúcidos " +"ou material de sustentação solúvel manual." + +#: src/libslic3r/PrintConfig.cpp:1057 +msgid "" +"This custom code is inserted at every layer change, right after the Z move " +"and before the extruder moves to the first layer point. Note that you can " +"use placeholder variables for all Slic3r settings as well as [layer_num] and " +"[layer_z]." +msgstr "" +"Este código personalizado é inserido em cada mudança de camada, logo após o " +"movimento Z e antes que a extrusora se mova para o primeiro ponto de camada. " +"Observe que você pode usar variáveis de espaço reservado para todas as " +"config. Slic3r, bem como [layer_num] e [layer_z]." + +#: src/libslic3r/PrintConfig.cpp:1068 +msgid "Supports remaining times" +msgstr "Tempo de impressão restante" + +#: src/libslic3r/PrintConfig.cpp:1069 +msgid "" +"Emit M73 P[percent printed] R[remaining time in minutes] at 1 minute " +"intervals into the G-code to let the firmware show accurate remaining time. " +"As of now only the Prusa i3 MK3 firmware recognizes M73. Also the i3 MK3 " +"firmware supports M73 Qxx Sxx for the silent mode." +msgstr "" +"Emita M73 P [porcentagem impressa] R [tempo restante em minutos] em " +"intervalos de 1 minuto no G-code para permitir que o firmware mostre o tempo " +"restante exato. A partir de agora apenas o firmware Prusa i3 MK3 reconhece " +"M73. Além disso, o firmware i3 MK3 suporta M73 QXX Sxx para o modo " +"silencioso." + +#: src/libslic3r/PrintConfig.cpp:1077 +msgid "Supports stealth mode" +msgstr "Suporta o modo silencioso" + +#: src/libslic3r/PrintConfig.cpp:1078 +msgid "The firmware supports stealth mode" +msgstr "O firmware suporta o modo silencioso" + +#: src/libslic3r/PrintConfig.cpp:1102 +msgid "Maximum feedrate X" +msgstr "Máxima taxa de alimentação do X" + +#: src/libslic3r/PrintConfig.cpp:1103 +msgid "Maximum feedrate Y" +msgstr "Máxima taxa de alimentação do Y" + +#: src/libslic3r/PrintConfig.cpp:1104 +msgid "Maximum feedrate Z" +msgstr "Máxima taxa de alimentação do Z" + +#: src/libslic3r/PrintConfig.cpp:1105 +msgid "Maximum feedrate E" +msgstr "Máxima taxa de alimentação do E" + +#: src/libslic3r/PrintConfig.cpp:1108 +msgid "Maximum feedrate of the X axis" +msgstr "Máxima taxa de alimentação do eixo X" + +#: src/libslic3r/PrintConfig.cpp:1109 +msgid "Maximum feedrate of the Y axis" +msgstr "Máxima taxa de alimentação do eixo Y" + +#: src/libslic3r/PrintConfig.cpp:1110 +msgid "Maximum feedrate of the Z axis" +msgstr "Máxima taxa de alimentação do eixo Z" + +#: src/libslic3r/PrintConfig.cpp:1111 +msgid "Maximum feedrate of the E axis" +msgstr "Máxima taxa de alimentação do eixo E" + +#: src/libslic3r/PrintConfig.cpp:1120 +msgid "Maximum acceleration X" +msgstr "Aceleração máxima do X" + +#: src/libslic3r/PrintConfig.cpp:1121 +msgid "Maximum acceleration Y" +msgstr "Aceleração máxima do Y" + +#: src/libslic3r/PrintConfig.cpp:1122 +msgid "Maximum acceleration Z" +msgstr "Aceleração máxima do Z" + +#: src/libslic3r/PrintConfig.cpp:1123 +msgid "Maximum acceleration E" +msgstr "Aceleração máxima do E" + +#: src/libslic3r/PrintConfig.cpp:1126 +msgid "Maximum acceleration of the X axis" +msgstr "Aceleração máxima do eixo X" + +#: src/libslic3r/PrintConfig.cpp:1127 +msgid "Maximum acceleration of the Y axis" +msgstr "Aceleração máxima do eixo Y" + +#: src/libslic3r/PrintConfig.cpp:1128 +msgid "Maximum acceleration of the Z axis" +msgstr "Aceleração máxima do eixo Z" + +#: src/libslic3r/PrintConfig.cpp:1129 +msgid "Maximum acceleration of the E axis" +msgstr "Aceleração máxima do eixo E" + +#: src/libslic3r/PrintConfig.cpp:1138 +msgid "Maximum jerk X" +msgstr "Máximo empurrão X" + +#: src/libslic3r/PrintConfig.cpp:1139 +msgid "Maximum jerk Y" +msgstr "Máximo empurrão Y" + +#: src/libslic3r/PrintConfig.cpp:1140 +msgid "Maximum jerk Z" +msgstr "Máximo empurrão Z" + +#: src/libslic3r/PrintConfig.cpp:1141 +msgid "Maximum jerk E" +msgstr "Máximo empurrão E" + +#: src/libslic3r/PrintConfig.cpp:1144 +msgid "Maximum jerk of the X axis" +msgstr "Máximo empurrão do eixo X" + +#: src/libslic3r/PrintConfig.cpp:1145 +msgid "Maximum jerk of the Y axis" +msgstr "Máximo empurrão do eixo Y" + +#: src/libslic3r/PrintConfig.cpp:1146 +msgid "Maximum jerk of the Z axis" +msgstr "Máximo empurrão do eixo Z" + +#: src/libslic3r/PrintConfig.cpp:1147 +msgid "Maximum jerk of the E axis" +msgstr "Máximo empurrão do eixo E" + +#: src/libslic3r/PrintConfig.cpp:1158 +msgid "Minimum feedrate when extruding" +msgstr "Taxa de alimentação mínima ao extrudar" + +#: src/libslic3r/PrintConfig.cpp:1160 +msgid "Minimum feedrate when extruding (M205 S)" +msgstr "Taxa de alimentação mínima ao extrudar (M205 S)" + +#: src/libslic3r/PrintConfig.cpp:1169 +msgid "Minimum travel feedrate" +msgstr "Taxa de alimentação mínima ao viajar" + +#: src/libslic3r/PrintConfig.cpp:1171 +msgid "Minimum travel feedrate (M205 T)" +msgstr "Taxa de alimentação mínima ao viajar (M205 T)" + +#: src/libslic3r/PrintConfig.cpp:1180 +msgid "Maximum acceleration when extruding" +msgstr "Aceleração máxima quando expurgando" + +#: src/libslic3r/PrintConfig.cpp:1182 +msgid "Maximum acceleration when extruding (M204 S)" +msgstr "Aceleração máxima quando extrudando (M204 S)" + +#: src/libslic3r/PrintConfig.cpp:1191 +msgid "Maximum acceleration when retracting" +msgstr "Aceleração máxima durante a retração" + +#: src/libslic3r/PrintConfig.cpp:1193 +msgid "Maximum acceleration when retracting (M204 T)" +msgstr "Aceleração máxima quando retração (M204 T)" + +#: src/libslic3r/PrintConfig.cpp:1201 src/libslic3r/PrintConfig.cpp:1210 +msgid "Max" +msgstr "Máx" + +#: src/libslic3r/PrintConfig.cpp:1202 +msgid "This setting represents the maximum speed of your fan." +msgstr "Esta config. representa a velocidade máxima da sua ventoinha." + +#: src/libslic3r/PrintConfig.cpp:1211 +#, c-format +msgid "" +"This is the highest printable layer height for this extruder, used to cap " +"the variable layer height and support layer height. Maximum recommended " +"layer height is 75% of the extrusion width to achieve reasonable inter-layer " +"adhesion. If set to 0, layer height is limited to 75% of the nozzle diameter." +msgstr "" +"Esta é a altura mais alta imprimível para esta extrusora, usada para tampar " +"a altura variável da camada e suportar a altura da camada. A altura " +"recomendada máxima da camada é 75% o da largura da extrusão para conseguir a " +"adesão razoável entre camadas. Se definido como 0, a altura da camada é " +"limitada a 75% o do diâmetro da ponteira." + +#: src/libslic3r/PrintConfig.cpp:1221 +msgid "Max print speed" +msgstr "Velocidade máxima de impressão" + +#: src/libslic3r/PrintConfig.cpp:1222 +msgid "" +"When setting other speed settings to 0 Slic3r will autocalculate the optimal " +"speed in order to keep constant extruder pressure. This experimental setting " +"is used to set the highest print speed you want to allow." +msgstr "" +"Ao definir outras config. de velocidade para 0, o Slic3r irá calcular " +"automaticamente a velocidade ideal, a fim de manter a pressão constante da " +"extrusora. Esta config. experimental é usada para definir a velocidade de " +"impressão mais alta que você deseja permitir." + +#: src/libslic3r/PrintConfig.cpp:1232 +msgid "" +"This experimental setting is used to set the maximum volumetric speed your " +"extruder supports." +msgstr "" +"Esta config. experimental é usada para definir a velocidade máxima " +"volumétrica que sua extrusora suporta." + +#: src/libslic3r/PrintConfig.cpp:1241 +msgid "Max volumetric slope positive" +msgstr "Inclinação volumétrica máx positiva" + +#: src/libslic3r/PrintConfig.cpp:1242 src/libslic3r/PrintConfig.cpp:1253 +msgid "" +"This experimental setting is used to limit the speed of change in extrusion " +"rate. A value of 1.8 mm³/s² ensures, that a change from the extrusion rate " +"of 1.8 mm³/s (0.45mm extrusion width, 0.2mm extrusion height, feedrate 20 mm/" +"s) to 5.4 mm³/s (feedrate 60 mm/s) will take at least 2 seconds." +msgstr "" +"Esta config. experimental é usada para limitar a velocidade de mudança na " +"taxa de extrusão. Um valor de 1,8 mm ³/s ² assegura que uma alteração da " +"taxa de extrusão de 1,8 mm ³/s (largura de extrusão de 0,45 mm, altura de " +"extrusão de 0,2 mm, avanço de 20 mm/s) para 5,4 mm ³/s (avanço 60 mm/s) " +"levará pelo menos 2 segundos." + +#: src/libslic3r/PrintConfig.cpp:1246 src/libslic3r/PrintConfig.cpp:1257 +msgid "mm³/s²" +msgstr "mm ³/s ²" + +#: src/libslic3r/PrintConfig.cpp:1252 +msgid "Max volumetric slope negative" +msgstr "Inclinação volumétrica máx negativa" + +#: src/libslic3r/PrintConfig.cpp:1264 src/libslic3r/PrintConfig.cpp:1273 +msgid "Min" +msgstr "Min" + +#: src/libslic3r/PrintConfig.cpp:1265 +msgid "This setting represents the minimum PWM your fan needs to work." +msgstr "" +"Esta config. representa o PWM mínimo que seu ventoinha precisa para " +"trabalhar." + +#: src/libslic3r/PrintConfig.cpp:1274 +msgid "" +"This is the lowest printable layer height for this extruder and limits the " +"resolution for variable layer height. Typical values are between 0.05 mm and " +"0.1 mm." +msgstr "" +"Esta é a altura mais baixa imprimível para esta extrusora e limita a " +"definição para a altura variável da camada. Os valores típicos são entre 0, " +"5 mm e 0,1 mm." + +#: src/libslic3r/PrintConfig.cpp:1282 +msgid "Min print speed" +msgstr "Velocidade mínima de impressão" + +#: src/libslic3r/PrintConfig.cpp:1283 +msgid "Slic3r will not scale speed down below this speed." +msgstr "Slic3r não vai escalar a velocidade abaixo desta velocidade." + +#: src/libslic3r/PrintConfig.cpp:1290 +msgid "Minimal filament extrusion length" +msgstr "Comprimento mínimo da extrusão do filamento" + +#: src/libslic3r/PrintConfig.cpp:1291 +msgid "" +"Generate no less than the number of skirt loops required to consume the " +"specified amount of filament on the bottom layer. For multi-extruder " +"machines, this minimum applies to each extruder." +msgstr "" +"Gerar não menos do que o número de voltas de saia necessários para consumir " +"a quantidade especificada de filamento na camada inferior. Para máquinas " +"multiextrusoras, este mínimo aplica-se a cada extrusora." + +#: src/libslic3r/PrintConfig.cpp:1300 +msgid "Configuration notes" +msgstr "Notas de config." + +#: src/libslic3r/PrintConfig.cpp:1301 +msgid "" +"You can put here your personal notes. This text will be added to the G-code " +"header comments." +msgstr "" +"Você pode colocar aqui suas anotações pessoais. Este texto será adicionado " +"aos comentários do cabeçalho do G-code." + +#: src/libslic3r/PrintConfig.cpp:1311 +msgid "" +"This is the diameter of your extruder nozzle (for example: 0.5, 0.35 etc.)" +msgstr "" +"Este é o diâmetro da ponteira da extrusora (por exemplo: 0.5, 0.35 etc.)" + +#: src/libslic3r/PrintConfig.cpp:1316 +msgid "Host Type" +msgstr "Tipo de host" + +#: src/libslic3r/PrintConfig.cpp:1317 +msgid "" +"Slic3r can upload G-code files to a printer host. This field must contain " +"the kind of the host." +msgstr "" +"Slic3r pode carregar arquivos de G-code para um host de impressora. Este " +"campo deve conter o tipo do host." + +#: src/libslic3r/PrintConfig.cpp:1328 +msgid "Only retract when crossing perimeters" +msgstr "Apenas retrair quando cruzar perímetros" + +#: src/libslic3r/PrintConfig.cpp:1329 +msgid "" +"Disables retraction when the travel path does not exceed the upper layer's " +"perimeters (and thus any ooze will be probably invisible)." +msgstr "" +"Desativa a retração quando o caminho de viagem não excede os perímetros da " +"camada superior (e, portanto, qualquer escorrimento será provavelmente " +"invisível)." + +#: src/libslic3r/PrintConfig.cpp:1336 +msgid "" +"This option will drop the temperature of the inactive extruders to prevent " +"oozing. It will enable a tall skirt automatically and move extruders outside " +"such skirt when changing temperatures." +msgstr "" +"Esta opção irá descartar a temperatura das extrusoras inativas para evitar a " +"escorrimento. Ele vai permitir uma saia alta automaticamente e mover " +"extrusoras fora de tal saia quando a mudança de temperatura." + +#: src/libslic3r/PrintConfig.cpp:1343 +msgid "Output filename format" +msgstr "Formato de nome de arquivo de saída" + +#: src/libslic3r/PrintConfig.cpp:1344 +msgid "" +"You can use all configuration options as variables inside this template. For " +"example: [layer_height], [fill_density] etc. You can also use [timestamp], " +"[year], [month], [day], [hour], [minute], [second], [version], " +"[input_filename], [input_filename_base]." +msgstr "" +"Você pode usar todas as opções de config. como variáveis dentro deste " +"modelo. Por exemplo: [camada_altura], [densidade_preenchimento] etc. Você " +"também pode usar [tempo], [ano], [mês], [dia], [hora], [minuto], [segundo], " +"[versão], [nome_entrada], [nome_entrada_base]." + +#: src/libslic3r/PrintConfig.cpp:1353 +msgid "Detect bridging perimeters" +msgstr "Detectar perímetros de ponte" + +#: src/libslic3r/PrintConfig.cpp:1355 +msgid "" +"Experimental option to adjust flow for overhangs (bridge flow will be used), " +"to apply bridge speed to them and enable fan." +msgstr "" +"Opção experimental para ajustar o fluxo para angulações (o fluxo da ponte " +"será usado), para aplicar a velocidade da ponte a eles e para habilitar a " +"ventoinha." + +#: src/libslic3r/PrintConfig.cpp:1361 +msgid "Filament parking position" +msgstr "Posição de estacionamento do filamento" + +#: src/libslic3r/PrintConfig.cpp:1362 +msgid "" +"Distance of the extruder tip from the position where the filament is parked " +"when unloaded. This should match the value in printer firmware." +msgstr "" +"Distância da ponta da extrusora da posição onde o filamento está estacionado " +"quando descarregado. Isso deve corresponder ao valor no firmware da " +"impressora." + +#: src/libslic3r/PrintConfig.cpp:1370 +msgid "Extra loading distance" +msgstr "Distância de carregamento extra" + +#: src/libslic3r/PrintConfig.cpp:1371 +msgid "" +"When set to zero, the distance the filament is moved from parking position " +"during load is exactly the same as it was moved back during unload. When " +"positive, it is loaded further, if negative, the loading move is shorter " +"than unloading." +msgstr "" +"Quando ajustado a zero, a distância que o filamento é movida da posição do " +"estacionamento durante a carga é exatamente a mesma que foi movida para trás " +"durante o descarregamento. Quando positivo, ele é carregado ainda mais, se " +"negativo, o movimento de carga é menor do que o descarregamento." + +#: src/libslic3r/PrintConfig.cpp:1379 src/libslic3r/PrintConfig.cpp:1397 +#: src/libslic3r/PrintConfig.cpp:1409 src/libslic3r/PrintConfig.cpp:1419 +msgid "Perimeters" +msgstr "Perímetros" + +#: src/libslic3r/PrintConfig.cpp:1380 +msgid "" +"This is the acceleration your printer will use for perimeters. A high value " +"like 9000 usually gives good results if your hardware is up to the job. Set " +"zero to disable acceleration control for perimeters." +msgstr "" +"Esta é a aceleração que sua impressora usará para perímetros. Um alto valor " +"como 9000 geralmente dá bons resultados se o seu hardware suporta. Defina " +"zero para desabilitar o controle de aceleração para perímetros." + +#: src/libslic3r/PrintConfig.cpp:1388 +msgid "Perimeter extruder" +msgstr "Extrusora de perímetro" + +#: src/libslic3r/PrintConfig.cpp:1390 +msgid "" +"The extruder to use when printing perimeters and brim. First extruder is 1." +msgstr "" +"A extrusora para usar ao imprimir perímetros e aba. A primeira extrusora é 1." + +#: src/libslic3r/PrintConfig.cpp:1399 +msgid "" +"Set this to a non-zero value to set a manual extrusion width for perimeters. " +"You may want to use thinner extrudates to get more accurate surfaces. If " +"left zero, default extrusion width will be used if set, otherwise 1.125 x " +"nozzle diameter will be used. If expressed as percentage (for example 200%) " +"it will be computed over layer height." +msgstr "" +"Defina isso como um valor diferente de zero para definir uma largura de " +"extrusão manual para perímetros. Você pode querer usar extrusões mais finos " +"para obter superfícies mais precisas. Se for deixado zero, a largura de " +"extrusão padrão será usada se definido, caso contrário, 1,125 x diâmetro da " +"ponteira será usado. Se expresso em porcentagem(por exemplo, 200%) Ele será " +"calculado sobre a altura da camada." + +#: src/libslic3r/PrintConfig.cpp:1411 +msgid "" +"Speed for perimeters (contours, aka vertical shells). Set to zero for auto." +msgstr "" +"Velocidade para perímetros (contornos, também chamadas de perímetros " +"externos verticais). Defina como zero para auto." + +#: src/libslic3r/PrintConfig.cpp:1421 +msgid "" +"This option sets the number of perimeters to generate for each layer. Note " +"that Slic3r may increase this number automatically when it detects sloping " +"surfaces which benefit from a higher number of perimeters if the Extra " +"Perimeters option is enabled." +msgstr "" +"Esta opção define o número de perímetros a gerar para cada camada. Observe " +"que o Slic3r pode aumentar esse número automaticamente quando detecta " +"superfícies inclinadas que se beneficiam de um número maior de perímetros se " +"a opção extra perímetros estiver habilitada." + +#: src/libslic3r/PrintConfig.cpp:1425 +msgid "(minimum)" +msgstr "(mínimo)" + +#: src/libslic3r/PrintConfig.cpp:1433 +msgid "" +"If you want to process the output G-code through custom scripts, just list " +"their absolute paths here. Separate multiple scripts with a semicolon. " +"Scripts will be passed the absolute path to the G-code file as the first " +"argument, and they can access the Slic3r config settings by reading " +"environment variables." +msgstr "" +"Se você quiser processar o G-code de saída por meio de scripts " +"personalizados, basta listar seus caminhos absolutos aqui. Separe vários " +"scripts com um ponto-e-vírgula. Os scripts serão passados o caminho absoluto " +"para o arquivo de G-code como o primeiro argumento, e eles poderão acessar " +"as config. de config. do Slic3r lendo variáveis de ambiente." + +#: src/libslic3r/PrintConfig.cpp:1445 +msgid "Printer type" +msgstr "Tipo de impressora" + +#: src/libslic3r/PrintConfig.cpp:1446 +msgid "Type of the printer." +msgstr "Tipo da impressora." + +#: src/libslic3r/PrintConfig.cpp:1451 +msgid "Printer notes" +msgstr "Notas da impressora" + +#: src/libslic3r/PrintConfig.cpp:1452 +msgid "You can put your notes regarding the printer here." +msgstr "Você pode colocar suas anotações sobre a impressora aqui." + +#: src/libslic3r/PrintConfig.cpp:1460 +msgid "Printer vendor" +msgstr "Fornecedor da impressora" + +#: src/libslic3r/PrintConfig.cpp:1461 +msgid "Name of the printer vendor." +msgstr "Nome do fornecedor da impressora." + +#: src/libslic3r/PrintConfig.cpp:1466 +msgid "Printer variant" +msgstr "Variante da impressora" + +#: src/libslic3r/PrintConfig.cpp:1467 +msgid "" +"Name of the printer variant. For example, the printer variants may be " +"differentiated by a nozzle diameter." +msgstr "" +"Nome da variante da impressora. Por exemplo, as variantes da impressora " +"podem ser diferenciadas por um diâmetro da ponteira." + +#: src/libslic3r/PrintConfig.cpp:1480 +msgid "Raft layers" +msgstr "Camadas da estrado" + +#: src/libslic3r/PrintConfig.cpp:1482 +msgid "" +"The object will be raised by this number of layers, and support material " +"will be generated under it." +msgstr "" +"O objeto será elevado por este número de camadas, e o material de suporte " +"será gerado em baixo dele." + +#: src/libslic3r/PrintConfig.cpp:1490 +msgid "Resolution" +msgstr "Resolução" + +#: src/libslic3r/PrintConfig.cpp:1491 +msgid "" +"Minimum detail resolution, used to simplify the input file for speeding up " +"the slicing job and reducing memory usage. High-resolution models often " +"carry more detail than printers can render. Set to zero to disable any " +"simplification and use full resolution from input." +msgstr "" +"Resolução de detalhes mínimos, usada para simplificar o arquivo de entrada " +"para acelerar o trabalho de fatiamento e reduzir o uso de memória. Modelos " +"de alta resolução geralmente carregam mais detalhes do que as impressoras " +"podem renderizar. Defina como zero para desabilitar qualquer simplificação e " +"usar a resolução completa da entrada." + +#: src/libslic3r/PrintConfig.cpp:1501 +msgid "Minimum travel after retraction" +msgstr "Retração em viagens acima de" + +#: src/libslic3r/PrintConfig.cpp:1502 +msgid "" +"Retraction is not triggered when travel moves are shorter than this length." +msgstr "" +"A retração não é acionada quando os movimentos de viagem são mais curtos que " +"esse comprimento." + +#: src/libslic3r/PrintConfig.cpp:1508 +msgid "Retract amount before wipe" +msgstr "Quantidade de retração antes da limpeza" + +#: src/libslic3r/PrintConfig.cpp:1509 +msgid "" +"With bowden extruders, it may be wise to do some amount of quick retract " +"before doing the wipe movement." +msgstr "" +"Com extrusoras Bowden, pode ser sábio fazer alguma quantidade de retração " +"rápida antes de fazer o movimento da limpeza." + +#: src/libslic3r/PrintConfig.cpp:1516 +msgid "Retract on layer change" +msgstr "Retrair na mudança de camada" + +#: src/libslic3r/PrintConfig.cpp:1517 +msgid "This flag enforces a retraction whenever a Z move is done." +msgstr "Este sinalizador impõe uma retração sempre que um movimento Z é feito." + +#: src/libslic3r/PrintConfig.cpp:1522 src/libslic3r/PrintConfig.cpp:1530 +msgid "Length" +msgstr "Comprimento" + +#: src/libslic3r/PrintConfig.cpp:1523 +msgid "Retraction Length" +msgstr "Comprimento de retração" + +#: src/libslic3r/PrintConfig.cpp:1524 +msgid "" +"When retraction is triggered, filament is pulled back by the specified " +"amount (the length is measured on raw filament, before it enters the " +"extruder)." +msgstr "" +"Quando a retração é acionada, o filamento é puxado para trás pela quantidade " +"especificada (o comprimento é medido em filamento cru, antes de entrar na " +"extrusora)." + +#: src/libslic3r/PrintConfig.cpp:1526 src/libslic3r/PrintConfig.cpp:1535 +msgid "mm (zero to disable)" +msgstr "mm (zero para desativar)" + +#: src/libslic3r/PrintConfig.cpp:1531 +msgid "Retraction Length (Toolchange)" +msgstr "Comprimento de retração (mudança de ferramenta)" + +#: src/libslic3r/PrintConfig.cpp:1532 +msgid "" +"When retraction is triggered before changing tool, filament is pulled back " +"by the specified amount (the length is measured on raw filament, before it " +"enters the extruder)." +msgstr "" +"Quando a retração é acionada antes de mudar de ferramenta, o filamento é " +"puxado para trás pela quantidade especificada (o comprimento é medido em " +"filamento cru, antes de entrar na extrusora)." + +#: src/libslic3r/PrintConfig.cpp:1540 +msgid "Lift Z" +msgstr "Elevar Z" + +#: src/libslic3r/PrintConfig.cpp:1541 +msgid "" +"If you set this to a positive value, Z is quickly raised every time a " +"retraction is triggered. When using multiple extruders, only the setting for " +"the first extruder will be considered." +msgstr "" +"Se você definir isso como um valor positivo, Z é rapidamente elevado sempre " +"que uma retração é acionada. Ao usar várias extrusoras, somente a config. " +"para a primeira extrusora será considerada." + +#: src/libslic3r/PrintConfig.cpp:1548 +msgid "Above Z" +msgstr "Acima de Z" + +#: src/libslic3r/PrintConfig.cpp:1549 +msgid "Only lift Z above" +msgstr "Apenas elevar Z acima" + +#: src/libslic3r/PrintConfig.cpp:1550 +msgid "" +"If you set this to a positive value, Z lift will only take place above the " +"specified absolute Z. You can tune this setting for skipping lift on the " +"first layers." +msgstr "" +"Se você definir isso como um valor positivo, o levante do Z só ocorrerá " +"acima do Z absoluto especificado. Você pode ajustar essa config. para pular " +"o elevador nas primeiras camadas." + +#: src/libslic3r/PrintConfig.cpp:1557 +msgid "Below Z" +msgstr "Abaixo de Z" + +#: src/libslic3r/PrintConfig.cpp:1558 +msgid "Only lift Z below" +msgstr "Apenas elevar Z abaixo" + +#: src/libslic3r/PrintConfig.cpp:1559 +msgid "" +"If you set this to a positive value, Z lift will only take place below the " +"specified absolute Z. You can tune this setting for limiting lift to the " +"first layers." +msgstr "" +"Se você definir isso como um valor positivo, o levante do Z só ocorrerá " +"abaixo do Z absoluto especificado. Você pode ajustar essa config. para " +"limitar a elevação às primeiras camadas." + +#: src/libslic3r/PrintConfig.cpp:1567 src/libslic3r/PrintConfig.cpp:1575 +msgid "Extra length on restart" +msgstr "Comprimento extra no reinício" + +#: src/libslic3r/PrintConfig.cpp:1568 +msgid "" +"When the retraction is compensated after the travel move, the extruder will " +"push this additional amount of filament. This setting is rarely needed." +msgstr "" +"Quando a retração é compensada após o movimento de viagem, a extrusora vai " +"empurrar esta quantidade adicional de filamento. Essa config. raramente é " +"necessária." + +#: src/libslic3r/PrintConfig.cpp:1576 +msgid "" +"When the retraction is compensated after changing tool, the extruder will " +"push this additional amount of filament." +msgstr "" +"Quando a retração é compensada após a ferramenta de mudança, a extrusora " +"empurrará esta quantidade adicional de filamento." + +#: src/libslic3r/PrintConfig.cpp:1583 src/libslic3r/PrintConfig.cpp:1584 +msgid "Retraction Speed" +msgstr "Velocidade da retração" + +#: src/libslic3r/PrintConfig.cpp:1585 +msgid "The speed for retractions (it only applies to the extruder motor)." +msgstr "A velocidade para retrações (aplica-se somente ao motor da extrusora)." + +#: src/libslic3r/PrintConfig.cpp:1591 src/libslic3r/PrintConfig.cpp:1592 +msgid "Deretraction Speed" +msgstr "Velocidade de retorno de retração" + +#: src/libslic3r/PrintConfig.cpp:1593 +msgid "" +"The speed for loading of a filament into extruder after retraction (it only " +"applies to the extruder motor). If left to zero, the retraction speed is " +"used." +msgstr "" +"A velocidade para o carregamento de um filamento na extrusora após a " +"retração (aplica-se somente ao motor da extrusora). Se deixada como zero, a " +"velocidade de retração é usada." + +#: src/libslic3r/PrintConfig.cpp:1600 +msgid "Seam position" +msgstr "Posição da costura" + +#: src/libslic3r/PrintConfig.cpp:1602 +msgid "Position of perimeters starting points." +msgstr "Posição inicial dos pontos do perímetro." + +#: src/libslic3r/PrintConfig.cpp:1608 +msgid "Random" +msgstr "Aleatório" + +#: src/libslic3r/PrintConfig.cpp:1609 +msgid "Nearest" +msgstr "Próximo" + +#: src/libslic3r/PrintConfig.cpp:1610 +msgid "Aligned" +msgstr "Alinhado(a)" + +#: src/libslic3r/PrintConfig.cpp:1618 +msgid "Direction" +msgstr "Direção" + +#: src/libslic3r/PrintConfig.cpp:1620 +msgid "Preferred direction of the seam" +msgstr "Direção preferida da costura" + +#: src/libslic3r/PrintConfig.cpp:1621 +msgid "Seam preferred direction" +msgstr "Direção de preferência da costura" + +#: src/libslic3r/PrintConfig.cpp:1628 +msgid "Jitter" +msgstr "Jitter" + +#: src/libslic3r/PrintConfig.cpp:1630 +msgid "Seam preferred direction jitter" +msgstr "Direção da costura preferencial para Jitter" + +#: src/libslic3r/PrintConfig.cpp:1631 +msgid "Preferred direction of the seam - jitter" +msgstr "Direção preferida da costura-jitter" + +#: src/libslic3r/PrintConfig.cpp:1641 +msgid "USB/serial port for printer connection." +msgstr "USB/porta serial para conexão da impressora." + +#: src/libslic3r/PrintConfig.cpp:1648 +msgid "Serial port speed" +msgstr "Velocidade da porta serial" + +#: src/libslic3r/PrintConfig.cpp:1649 +msgid "Speed (baud) of USB/serial port for printer connection." +msgstr "Velocidade (baud) do USB/porta serial para conexão da impressora." + +#: src/libslic3r/PrintConfig.cpp:1658 +msgid "Distance from object" +msgstr "Distância do objeto" + +#: src/libslic3r/PrintConfig.cpp:1659 +msgid "" +"Distance between skirt and object(s). Set this to zero to attach the skirt " +"to the object(s) and get a brim for better adhesion." +msgstr "" +"Distância entre a saia e o objeto (s). Defina isso como zero para anexar a " +"saia para o objeto (s) e obter uma aba para uma melhor aderência." + +#: src/libslic3r/PrintConfig.cpp:1666 +msgid "Skirt height" +msgstr "Altura da saia" + +#: src/libslic3r/PrintConfig.cpp:1667 +msgid "" +"Height of skirt expressed in layers. Set this to a tall value to use skirt " +"as a shield against drafts." +msgstr "" +"Altura da saia expressa em camadas. Defina isso como um valor alto para usar " +"a saia como um escudo contra rascunhos." + +#: src/libslic3r/PrintConfig.cpp:1674 +msgid "Loops (minimum)" +msgstr "Voltas (mínimo)" + +#: src/libslic3r/PrintConfig.cpp:1675 +msgid "Skirt Loops" +msgstr "Voltas de saia" + +#: src/libslic3r/PrintConfig.cpp:1676 +msgid "" +"Number of loops for the skirt. If the Minimum Extrusion Length option is " +"set, the number of loops might be greater than the one configured here. Set " +"this to zero to disable skirt completely." +msgstr "" +"Número de voltas para a saia. Se a opção comprimento mínimo de extrusão " +"estiver definida, o número de voltas pode ser maior do que aquele " +"configurado aqui. Defina isso como zero para desabilitar a saia " +"completamente." + +#: src/libslic3r/PrintConfig.cpp:1684 +msgid "Slow down if layer print time is below" +msgstr "Diminuir a velocidade quando o tempo de impressão for menor que" + +#: src/libslic3r/PrintConfig.cpp:1685 +msgid "" +"If layer print time is estimated below this number of seconds, print moves " +"speed will be scaled down to extend duration to this value." +msgstr "" +"Se o tempo de impressão da camada for estimado abaixo desse número de " +"segundos, a velocidade de impressão será reduzida para estender a duração a " +"esse valor." + +#: src/libslic3r/PrintConfig.cpp:1695 +msgid "Small perimeters" +msgstr "Perímetro pequeno" + +#: src/libslic3r/PrintConfig.cpp:1697 +msgid "" +"This separate setting will affect the speed of perimeters having radius <= " +"6.5mm (usually holes). If expressed as percentage (for example: 80%) it will " +"be calculated on the perimeters speed setting above. Set to zero for auto." +msgstr "" +"Este ajuste separado afetará a velocidade dos perímetros que têm o raio < = " +"6.5 mm (geralmente furos). Se expresso em porcentagem(por exemplo: 80%) Ele " +"será calculado sobre a velocidade de perímetros configurados acima. Defina " +"como zero para auto." + +#: src/libslic3r/PrintConfig.cpp:1707 +msgid "Solid infill threshold area" +msgstr "Área de limiar de preenchimento sólido" + +#: src/libslic3r/PrintConfig.cpp:1709 +msgid "" +"Force solid infill for regions having a smaller area than the specified " +"threshold." +msgstr "" +"Forçar preenchimento sólido para regiões com uma área menor do que o limite " +"especificado." + +#: src/libslic3r/PrintConfig.cpp:1710 +msgid "mm²" +msgstr "mm²" + +#: src/libslic3r/PrintConfig.cpp:1716 +msgid "Solid infill extruder" +msgstr "Extrusora de preenchimento sólido" + +#: src/libslic3r/PrintConfig.cpp:1718 +msgid "The extruder to use when printing solid infill." +msgstr "" +"A extrusora a ser utilizada quando estiver imprimindo preenchimento sólido." + +#: src/libslic3r/PrintConfig.cpp:1724 +msgid "Solid infill every" +msgstr "Preenchimento sólido a cada" + +#: src/libslic3r/PrintConfig.cpp:1726 +msgid "" +"This feature allows to force a solid layer every given number of layers. " +"Zero to disable. You can set this to any value (for example 9999); Slic3r " +"will automatically choose the maximum possible number of layers to combine " +"according to nozzle diameter and layer height." +msgstr "" +"Este recurso permite forçar uma camada sólida a cada número determinado de " +"camadas. Zero para desabilitar. Você pode definir isso para qualquer valor " +"(por exemplo 9999); Slic3r escolherá automaticamente o número máximo " +"possível de camadas para combinar de acordo com o diâmetro da ponteira e a " +"altura da camada." + +#: src/libslic3r/PrintConfig.cpp:1738 +msgid "" +"Set this to a non-zero value to set a manual extrusion width for infill for " +"solid surfaces. If left zero, default extrusion width will be used if set, " +"otherwise 1.125 x nozzle diameter will be used. If expressed as percentage " +"(for example 90%) it will be computed over layer height." +msgstr "" +"Defina isso como um valor diferente de zero para definir uma largura de " +"extrusão manual para preenchimento de superfícies sólidas. Se for deixado " +"zero, a largura de extrusão padrão será usada se definido, caso contrário, " +"1,125 x diâmetro da ponteira será usado. Se expresso em porcentagem(por " +"exemplo, 90%) Ele será calculado sobre a altura da camada." + +#: src/libslic3r/PrintConfig.cpp:1748 +msgid "" +"Speed for printing solid regions (top/bottom/internal horizontal shells). " +"This can be expressed as a percentage (for example: 80%) over the default " +"infill speed above. Set to zero for auto." +msgstr "" +"Velocidade para imprimir regiões sólidas (topo/fundo/perímetros externos " +"horizontais internas). Isto pode ser expresso em porcentagem(por exemplo: " +"80%) sobre a velocidade de preenchimento padrão acima. Defina como zero para " +"auto." + +#: src/libslic3r/PrintConfig.cpp:1760 +msgid "Number of solid layers to generate on top and bottom surfaces." +msgstr "" +"Número de camadas sólidas a serem geradas nas interfaces do topo e base." + +#: src/libslic3r/PrintConfig.cpp:1766 +msgid "Spiral vase" +msgstr "Vaso espiral" + +#: src/libslic3r/PrintConfig.cpp:1767 +msgid "" +"This feature will raise Z gradually while printing a single-walled object in " +"order to remove any visible seam. This option requires a single perimeter, " +"no infill, no top solid layers and no support material. You can still set " +"any number of bottom solid layers as well as skirt/brim loops. It won't work " +"when printing more than an object." +msgstr "" +"Este recurso irá elevar Z gradualmente durante a impressão de um objeto de " +"parede única, a fim de remover qualquer costura visível. Esta opção exige um " +"único perímetro, nenhum preenchimento, nenhumas camadas contínuas superiores " +"e nenhum material de sustentação. Você ainda pode definir qualquer número de " +"camadas sólidas de fundo, bem como saia/aba voltas. Ele não funcionará ao " +"imprimir mais de um objeto." + +#: src/libslic3r/PrintConfig.cpp:1775 +msgid "Temperature variation" +msgstr "Variação de temperatura" + +#: src/libslic3r/PrintConfig.cpp:1776 +msgid "" +"Temperature difference to be applied when an extruder is not active. Enables " +"a full-height \"sacrificial\" skirt on which the nozzles are periodically " +"wiped." +msgstr "" +"Diferença de temperatura a ser aplicada quando uma extrusora não está ativa. " +"Permite uma saia \"sacrificial\" em que as ponteiras são limpadas " +"periodicamente." + +#: src/libslic3r/PrintConfig.cpp:1786 +msgid "" +"This start procedure is inserted at the beginning, after bed has reached the " +"target temperature and extruder just started heating, and before extruder " +"has finished heating. If PrusaSlicer detects M104 or M190 in your custom " +"codes, such commands will not be prepended automatically so you're free to " +"customize the order of heating commands and other custom actions. Note that " +"you can use placeholder variables for all PrusaSlicer settings, so you can " +"put a \"M109 S[first_layer_temperature]\" command wherever you want." +msgstr "" +"Este procedimento do começo é introduzido no início, depois que a mesa " +"alcançou a temperatura alvo e a extrusora apenas começou o aquecimento, e " +"antes que a extrusora terminasse o aquecimento. Se PrusaSlicer detecta M104 " +"ou M190 em seus códigos personalizados, esses comandos não serão precedidos " +"automaticamente para que você esteja livre para personalizar a ordem dos " +"comandos de aquecimento e outras ações personalizadas. Observe que você pode " +"usar variáveis de espaço reservado para todas as config. de PrusaSlicer, " +"para que você possa colocar um comando \"M109 S " +"[temperatura_primeira_camada]\" onde quiser." + +#: src/libslic3r/PrintConfig.cpp:1801 +msgid "" +"This start procedure is inserted at the beginning, after any printer start " +"gcode (and after any toolchange to this filament in case of multi-material " +"printers). This is used to override settings for a specific filament. If " +"PrusaSlicer detects M104, M109, M140 or M190 in your custom codes, such " +"commands will not be prepended automatically so you're free to customize the " +"order of heating commands and other custom actions. Note that you can use " +"placeholder variables for all PrusaSlicer settings, so you can put a \"M109 " +"S[first_layer_temperature]\" command wherever you want. If you have multiple " +"extruders, the gcode is processed in extruder order." +msgstr "" +"Este procedimento de início é inserido no começo, depois de qualquer " +"impressora iniciar Gcode (e depois de qualquer troca de ferramenta para este " +"filamento em caso de impressoras de vários materiais). Isso é usado para " +"substituir as config. de um filamento específico. Se PrusaSlicer detecta " +"M104, M109, M140 ou M190 em seus códigos personalizados, esses comandos não " +"serão precedidos automaticamente para que você esteja livre para " +"personalizar a ordem dos comandos de aquecimento e outras ações " +"personalizadas. Observe que você pode usar variáveis de espaço reservado " +"para todas as config. de PrusaSlicer, para que você possa colocar um comando " +"\"M109 S [temperatura_primeira_camada]\" onde quiser. Se você tiver várias " +"extrusoras, o Gcode é processado em ordem de extrusora." + +#: src/libslic3r/PrintConfig.cpp:1817 +msgid "Single Extruder Multi Material" +msgstr "Única extrusora multi material" + +#: src/libslic3r/PrintConfig.cpp:1818 +msgid "The printer multiplexes filaments into a single hot end." +msgstr "A impressora multiplexes filamentos em uma única extremidade quente." + +#: src/libslic3r/PrintConfig.cpp:1823 +msgid "Prime all printing extruders" +msgstr "Extrusar todas as extrusoras de impressão" + +#: src/libslic3r/PrintConfig.cpp:1824 +msgid "" +"If enabled, all printing extruders will be primed at the front edge of the " +"print bed at the start of the print." +msgstr "" +"Se ativada, todas as extrusoras de impressão extrusarão na aba dianteira da " +"mesa de impressão no início da impressão." + +#: src/libslic3r/PrintConfig.cpp:1829 +msgid "Generate support material" +msgstr "Gerar material de suporte" + +#: src/libslic3r/PrintConfig.cpp:1831 +msgid "Enable support material generation." +msgstr "Habilitar geração de material de suporte." + +#: src/libslic3r/PrintConfig.cpp:1835 +msgid "Auto generated supports" +msgstr "Gerar suportes automaticamente" + +#: src/libslic3r/PrintConfig.cpp:1837 +msgid "" +"If checked, supports will be generated automatically based on the overhang " +"threshold value. If unchecked, supports will be generated inside the " +"\"Support Enforcer\" volumes only." +msgstr "" +"Se marcada, os suportes serão gerados automaticamente com base no valor do " +"limite de angulação. Se desmarcada, as sustentações serão geradas dentro dos " +"volumes do \"reforçador de suporte\" somente." + +#: src/libslic3r/PrintConfig.cpp:1843 +msgid "XY separation between an object and its support" +msgstr "Separação entre o objeto e seu suporte em XY" + +#: src/libslic3r/PrintConfig.cpp:1845 +msgid "" +"XY separation between an object and its support. If expressed as percentage " +"(for example 50%), it will be calculated over external perimeter width." +msgstr "" +"Separação entre o objeto e seu suporte em XY. Se expresso como porcentagem " +"(por exemplo, 50%), será calculado com base na espessura do perímetro " +"externo." + +#: src/libslic3r/PrintConfig.cpp:1855 +msgid "Pattern angle" +msgstr "Ângulo do padrão" + +#: src/libslic3r/PrintConfig.cpp:1857 +msgid "" +"Use this setting to rotate the support material pattern on the horizontal " +"plane." +msgstr "" +"Use essa config. para girar o padrão de material de suporte no plano " +"horizontal." + +#: src/libslic3r/PrintConfig.cpp:1867 src/libslic3r/PrintConfig.cpp:2563 +msgid "" +"Only create support if it lies on a build plate. Don't create support on a " +"print." +msgstr "" +"Apenas criar suporte se ele está em uma mesa. Não crie suporte em uma " +"impressão." + +#: src/libslic3r/PrintConfig.cpp:1873 +msgid "Contact Z distance" +msgstr "Distância de contato Z" + +#: src/libslic3r/PrintConfig.cpp:1875 +msgid "" +"The vertical distance between object and support material interface. Setting " +"this to 0 will also prevent Slic3r from using bridge flow and speed for the " +"first object layer." +msgstr "" +"A distância vertical entre o objeto e a interface de material de suporte. " +"Definir isso como 0 também impedirá Slic3r de usar o fluxo de ponte e a " +"velocidade para a primeira camada de objeto." + +#: src/libslic3r/PrintConfig.cpp:1882 +msgid "0 (soluble)" +msgstr "0 (solúvel)" + +#: src/libslic3r/PrintConfig.cpp:1883 +msgid "0.2 (detachable)" +msgstr "0.2 (destacável)" + +#: src/libslic3r/PrintConfig.cpp:1888 +msgid "Enforce support for the first" +msgstr "Reforçar suportes para a(s) primeira(s)" + +#: src/libslic3r/PrintConfig.cpp:1890 +msgid "" +"Generate support material for the specified number of layers counting from " +"bottom, regardless of whether normal support material is enabled or not and " +"regardless of any angle threshold. This is useful for getting more adhesion " +"of objects having a very thin or poor footprint on the build plate." +msgstr "" +"Gere material de suporte para o número especificado de camadas que contam da " +"parte inferior, independentemente de o material de suporte normal estar " +"ativado ou não e independentemente de qualquer limite de ângulo. Isso é útil " +"para obter mais aderência de objetos com uma pegada muito fina ou fraca na " +"placa de construção." + +#: src/libslic3r/PrintConfig.cpp:1895 +msgid "Enforce support for the first n layers" +msgstr "Reforçar suportes na(s) primera(s) n camada(s)" + +#: src/libslic3r/PrintConfig.cpp:1901 +msgid "Support material/raft/skirt extruder" +msgstr "Extrusora de material de suporte/estrado/saia" + +#: src/libslic3r/PrintConfig.cpp:1903 +msgid "" +"The extruder to use when printing support material, raft and skirt (1+, 0 to " +"use the current extruder to minimize tool changes)." +msgstr "" +"A extrusora a ser usada ao imprimir material de suporte, estrado e saia (1 " +"+, 0 para usar a extrusora atual para minimizar as mudanças na ferramenta)." + +#: src/libslic3r/PrintConfig.cpp:1912 +msgid "" +"Set this to a non-zero value to set a manual extrusion width for support " +"material. If left zero, default extrusion width will be used if set, " +"otherwise nozzle diameter will be used. If expressed as percentage (for " +"example 90%) it will be computed over layer height." +msgstr "" +"Defina isso como um valor diferente de zero para definir uma largura de " +"extrusão manual para material de suporte. Se deixada em zero, a largura " +"padrão da extrusão será usada, se não o diâmetro da ponteira será usado. Se " +"expresso em porcentagem(por exemplo, 90%) Ele será calculado sobre a altura " +"da camada." + +#: src/libslic3r/PrintConfig.cpp:1920 +msgid "Interface loops" +msgstr "Voltas da interface" + +#: src/libslic3r/PrintConfig.cpp:1922 +msgid "" +"Cover the top contact layer of the supports with loops. Disabled by default." +msgstr "" +"Cubra a camada de contato superior dos suportes com laços. Desativado por " +"padrão." + +#: src/libslic3r/PrintConfig.cpp:1927 +msgid "Support material/raft interface extruder" +msgstr "Extrusora de material de suporte/estrado" + +#: src/libslic3r/PrintConfig.cpp:1929 +msgid "" +"The extruder to use when printing support material interface (1+, 0 to use " +"the current extruder to minimize tool changes). This affects raft too." +msgstr "" +"A extrusora para usar ao imprimir a relação material do apoio (1 +, 0 para " +"usar o extrusor atual para minimizar mudanças da ferramenta). Isso afeta o " +"estrado também." + +#: src/libslic3r/PrintConfig.cpp:1936 +msgid "Interface layers" +msgstr "Camadas de interface" + +#: src/libslic3r/PrintConfig.cpp:1938 +msgid "" +"Number of interface layers to insert between the object(s) and support " +"material." +msgstr "" +"Número de camadas de interface para inserir entre o objeto(s) e material de " +"suporte." + +#: src/libslic3r/PrintConfig.cpp:1945 +msgid "Interface pattern spacing" +msgstr "Espaçamento do padrão da interface" + +#: src/libslic3r/PrintConfig.cpp:1947 +msgid "Spacing between interface lines. Set zero to get a solid interface." +msgstr "" +"Espaçamento entre as linhas de interface. Defina zero para obter uma " +"interface sólida." + +#: src/libslic3r/PrintConfig.cpp:1956 +msgid "" +"Speed for printing support material interface layers. If expressed as " +"percentage (for example 50%) it will be calculated over support material " +"speed." +msgstr "" +"Velocidade para camadas de interface de material de suporte de impressão. Se " +"expresso em porcentagem(por exemplo, 50%) Ele será calculado sobre a " +"velocidade do material de suporte." + +#: src/libslic3r/PrintConfig.cpp:1965 +msgid "Pattern" +msgstr "Padrão" + +#: src/libslic3r/PrintConfig.cpp:1967 +msgid "Pattern used to generate support material." +msgstr "Padrão usado para gerar material de suporte." + +#: src/libslic3r/PrintConfig.cpp:1973 +msgid "Rectilinear grid" +msgstr "Grade rectilínea" + +#: src/libslic3r/PrintConfig.cpp:1979 +msgid "Pattern spacing" +msgstr "Padrão de espaçamento" + +#: src/libslic3r/PrintConfig.cpp:1981 +msgid "Spacing between support material lines." +msgstr "Espaçamento entre linhas de material de suporte." + +#: src/libslic3r/PrintConfig.cpp:1990 +msgid "Speed for printing support material." +msgstr "Velocidade para imprimir material de suporte." + +#: src/libslic3r/PrintConfig.cpp:1997 +msgid "Synchronize with object layers" +msgstr "Sincronizar com camadas de objeto" + +#: src/libslic3r/PrintConfig.cpp:1999 +msgid "" +"Synchronize support layers with the object print layers. This is useful with " +"multi-material printers, where the extruder switch is expensive." +msgstr "" +"Sincronize camadas de suporte com as camadas de impressão do objeto. Isto é " +"útil com as impressoras do multi-material, onde o interruptor da extrusora é " +"caro." + +#: src/libslic3r/PrintConfig.cpp:2005 +msgid "Overhang threshold" +msgstr "Limite de angulação" + +#: src/libslic3r/PrintConfig.cpp:2007 +msgid "" +"Support material will not be generated for overhangs whose slope angle (90° " +"= vertical) is above the given threshold. In other words, this value " +"represent the most horizontal slope (measured from the horizontal plane) " +"that you can print without support material. Set to zero for automatic " +"detection (recommended)." +msgstr "" +"O material de suporte não será gerado para angulações cujo ângulo de " +"inclinação (90 ° = vertical) esteja acima do limite determinado. Em outras " +"palavras, esse valor representa a inclinação mais horizontal (medida a " +"partir do plano horizontal) que você pode imprimir sem material de suporte. " +"Defina como zero para detecção automática (recomendado)." + +#: src/libslic3r/PrintConfig.cpp:2019 +msgid "With sheath around the support" +msgstr "Com bainha em torno do apoio" + +#: src/libslic3r/PrintConfig.cpp:2021 +msgid "" +"Add a sheath (a single perimeter line) around the base support. This makes " +"the support more reliable, but also more difficult to remove." +msgstr "" +"Adicione uma bainha (uma única linha de perímetro) em torno do suporte base. " +"Isso torna o suporte mais confiável, mas também mais difícil de remover." + +#: src/libslic3r/PrintConfig.cpp:2028 +msgid "" +"Extruder temperature for layers after the first one. Set this to zero to " +"disable temperature control commands in the output." +msgstr "" +"Temperatura da extrusora para camadas após a primeira. Defina como zero para " +"desabilitar os comandos de controle de temperatura na saída." + +#: src/libslic3r/PrintConfig.cpp:2036 +msgid "Detect thin walls" +msgstr "Detectar paredes finas" + +#: src/libslic3r/PrintConfig.cpp:2038 +msgid "" +"Detect single-width walls (parts where two extrusions don't fit and we need " +"to collapse them into a single trace)." +msgstr "" +"Detecte paredes de largura única (partes onde duas extrusões não cabem e " +"precisamos recolhê-las em um único traço)." + +#: src/libslic3r/PrintConfig.cpp:2044 +msgid "Threads" +msgstr "Roscas" + +#: src/libslic3r/PrintConfig.cpp:2045 +msgid "" +"Threads are used to parallelize long-running tasks. Optimal threads number " +"is slightly above the number of available cores/processors." +msgstr "" +"Tópicos são usados para paralelizar tarefas de execução demorada. O número " +"de tópicos ideais está ligeiramente acima do número de núcleos/processadores " +"disponíveis." + +#: src/libslic3r/PrintConfig.cpp:2057 +msgid "" +"This custom code is inserted before every toolchange. Placeholder variables " +"for all PrusaSlicer settings as well as {previous_extruder} and " +"{next_extruder} can be used. When a tool-changing command which changes to " +"the correct extruder is included (such as T{next_extruder}), PrusaSlicer " +"will emit no other such command. It is therefore possible to script custom " +"behaviour both before and after the toolchange." +msgstr "" +"Este código personalizado é inserido antes de cada troca de ferramenta. " +"Variáveis de espaço reservado para todas as config. de PrusaSlicer, bem como " +"{previous_extruder} e {next_extruder} podem ser usadas. Quando um comando de " +"mudança de ferramenta que muda para a extrusora correta está incluído (como " +"T {next_extruder}), PrusaSlicer emitirá nenhum outro comando tal. Portanto, " +"é possível script comportamento personalizado antes e depois da mudança de " +"ferramenta." + +#: src/libslic3r/PrintConfig.cpp:2070 +msgid "" +"Set this to a non-zero value to set a manual extrusion width for infill for " +"top surfaces. You may want to use thinner extrudates to fill all narrow " +"regions and get a smoother finish. If left zero, default extrusion width " +"will be used if set, otherwise nozzle diameter will be used. If expressed as " +"percentage (for example 90%) it will be computed over layer height." +msgstr "" +"Defina isso como um valor diferente de zero para definir uma largura de " +"extrusão manual para preenchimento para superfícies superiores. Você pode " +"querer usar extrusões mais finos para preencher todas as regiões estreitas e " +"obter um acabamento mais suave. Se a esquerda zero, a largura padrão da " +"extrusão será usada se ajustado, se não o diâmetro da ponteira será usado. " +"Se expresso em porcentagem(por exemplo, 90%) Ele será calculado sobre a " +"altura da camada." + +#: src/libslic3r/PrintConfig.cpp:2081 +msgid "" +"Speed for printing top solid layers (it only applies to the uppermost " +"external layers and not to their internal solid layers). You may want to " +"slow down this to get a nicer surface finish. This can be expressed as a " +"percentage (for example: 80%) over the solid infill speed above. Set to zero " +"for auto." +msgstr "" +"Velocidade para imprimir camadas sólidas superiores (só se aplica às camadas " +"externas superiores e não às suas camadas sólidas internas). Você pode " +"querer diminuir este para ter um revestimento de superfície mais agradável. " +"Isto pode ser expresso em porcentagem(por exemplo: 80%) sobre a velocidade " +"de preenchimento sólido acima. Defina como zero para auto." + +#: src/libslic3r/PrintConfig.cpp:2096 +msgid "Number of solid layers to generate on top surfaces." +msgstr "Número de camadas sólidas para gerar em superfícies superiores." + +#: src/libslic3r/PrintConfig.cpp:2097 +msgid "Top solid layers" +msgstr "Camadas sólidas de topo" + +#: src/libslic3r/PrintConfig.cpp:2103 +msgid "Speed for travel moves (jumps between distant extrusion points)." +msgstr "" +"Velocidade para movimentos de viagem (saltos entre pontos de extrusão " +"distantes)." + +#: src/libslic3r/PrintConfig.cpp:2111 +msgid "Use firmware retraction" +msgstr "Usar retração do firmware" + +#: src/libslic3r/PrintConfig.cpp:2112 +msgid "" +"This experimental setting uses G10 and G11 commands to have the firmware " +"handle the retraction. This is only supported in recent Marlin." +msgstr "" +"Esta config. experimental usa comandos G10 e G11 para que o firmware " +"manipule a retração. Isso só é suportado no recente Marlin." + +#: src/libslic3r/PrintConfig.cpp:2118 +msgid "Use relative E distances" +msgstr "Utilizar distâncias relativas do E" + +#: src/libslic3r/PrintConfig.cpp:2119 +msgid "" +"If your firmware requires relative E values, check this, otherwise leave it " +"unchecked. Most firmwares use absolute values." +msgstr "" +"Se o firmware necessitar de valores relativos E, verifique isto, caso " +"contrário, deixe-o desmarcado. A maioria dos firmwares usa valores absolutos." + +#: src/libslic3r/PrintConfig.cpp:2125 +msgid "Use volumetric E" +msgstr "Usar E volumétrico" + +#: src/libslic3r/PrintConfig.cpp:2126 +msgid "" +"This experimental setting uses outputs the E values in cubic millimeters " +"instead of linear millimeters. If your firmware doesn't already know " +"filament diameter(s), you can put commands like 'M200 D[filament_diameter_0] " +"T0' in your start G-code in order to turn volumetric mode on and use the " +"filament diameter associated to the filament selected in Slic3r. This is " +"only supported in recent Marlin." +msgstr "" +"Essa config. experimental usa saídas os valores E em milímetros cúbicos em " +"vez de milímetros lineares. Se o firmware já não souber o diâmetro (s) do " +"filamento, você pode colocar comandos como ' m 200 D [filament_diameter_0] " +"T0 ' no seu G-code inicial para ativar o modo volumétrico e usar o diâmetro " +"do filamento associado ao filamento selecionado em Slic3r. Isso só é " +"suportado no recente Marlin." + +#: src/libslic3r/PrintConfig.cpp:2136 +msgid "Enable variable layer height feature" +msgstr "Habilitar altura de camada variável" + +#: src/libslic3r/PrintConfig.cpp:2137 +msgid "" +"Some printers or printer setups may have difficulties printing with a " +"variable layer height. Enabled by default." +msgstr "" +"Algumas impressoras ou config. de impressora podem ter dificuldades para " +"imprimir com uma altura de camada variável. Ativado por padrão." + +#: src/libslic3r/PrintConfig.cpp:2143 +msgid "Wipe while retracting" +msgstr "Limpe durante a retração" + +#: src/libslic3r/PrintConfig.cpp:2144 +msgid "" +"This flag will move the nozzle while retracting to minimize the possible " +"blob on leaky extruders." +msgstr "" +"Esta bandeira moverá a ponteira ao retrair para minimizar a bolha possível " +"em extrusoras vazando." + +#: src/libslic3r/PrintConfig.cpp:2151 +msgid "" +"Multi material printers may need to prime or purge extruders on tool " +"changes. Extrude the excess material into the wipe tower." +msgstr "" +"Várias impressoras de multi-material podem precisar purgar extrusoras em " +"alterações de ferramenta. EXTRUDE o excesso de material para a torre de " +"limpeza." + +#: src/libslic3r/PrintConfig.cpp:2157 +msgid "Purging volumes - load/unload volumes" +msgstr "Volumes de purga-volumes de carga/descarregamento" + +#: src/libslic3r/PrintConfig.cpp:2158 +msgid "" +"This vector saves required volumes to change from/to each tool used on the " +"wipe tower. These values are used to simplify creation of the full purging " +"volumes below." +msgstr "" +"Este vetor salva os volumes necessários para mudar de/para cada ferramenta " +"usada na torre de limpeza. Esses valores são usados para simplificar a " +"criação dos volumes de purga completos abaixo." + +#: src/libslic3r/PrintConfig.cpp:2164 +msgid "Purging volumes - matrix" +msgstr "Volumes de purga-matriz" + +#: src/libslic3r/PrintConfig.cpp:2165 +msgid "" +"This matrix describes volumes (in cubic milimetres) required to purge the " +"new filament on the wipe tower for any given pair of tools." +msgstr "" +"Esta matriz descreve volumes (em milimetros cúbicos) necessários para limpar " +"o novo filamento na torre de limpeza para qualquer dado par de ferramentas." + +#: src/libslic3r/PrintConfig.cpp:2174 +msgid "Position X" +msgstr "Posição X" + +#: src/libslic3r/PrintConfig.cpp:2175 +msgid "X coordinate of the left front corner of a wipe tower" +msgstr "Coordenada X do canto frontal esquerdo de uma torre de limpeza" + +#: src/libslic3r/PrintConfig.cpp:2181 +msgid "Position Y" +msgstr "Posição Y" + +#: src/libslic3r/PrintConfig.cpp:2182 +msgid "Y coordinate of the left front corner of a wipe tower" +msgstr "Coordenada Y do canto dianteiro esquerdo de uma torre de limpeza" + +#: src/libslic3r/PrintConfig.cpp:2189 +msgid "Width of a wipe tower" +msgstr "Largura de uma torre da limpeza" + +#: src/libslic3r/PrintConfig.cpp:2195 +msgid "Wipe tower rotation angle" +msgstr "Ângulo de rotação da torre" + +#: src/libslic3r/PrintConfig.cpp:2196 +msgid "Wipe tower rotation angle with respect to x-axis." +msgstr "Ângulo de rotação da torre de limpeza em relação ao eixo X." + +#: src/libslic3r/PrintConfig.cpp:2203 +msgid "Wipe into this object's infill" +msgstr "Limpe no preenchimento deste objeto" + +#: src/libslic3r/PrintConfig.cpp:2204 +msgid "" +"Purging after toolchange will done inside this object's infills. This lowers " +"the amount of waste but may result in longer print time due to additional " +"travel moves." +msgstr "" +"Purga após troca de ferramenta será feito dentro de preenchimentos deste " +"objeto. Isso diminui a quantidade de resíduos, mas pode resultar em tempo de " +"impressão mais longo devido a movimentos de viagem adicionais." + +#: src/libslic3r/PrintConfig.cpp:2211 +msgid "Wipe into this object" +msgstr "Limpar neste objeto" + +#: src/libslic3r/PrintConfig.cpp:2212 +msgid "" +"Object will be used to purge the nozzle after a toolchange to save material " +"that would otherwise end up in the wipe tower and decrease print time. " +"Colours of the objects will be mixed as a result." +msgstr "" +"Objeto será usado para limpar o bico após uma troca de ferramenta para " +"salvar o material que de outra forma acabaria na torre de limpeza e diminuir " +"o tempo de impressão. As cores dos objetos serão misturadas como resultado." + +#: src/libslic3r/PrintConfig.cpp:2218 +msgid "Maximal bridging distance" +msgstr "Distância de ponte máxima" + +#: src/libslic3r/PrintConfig.cpp:2219 +msgid "Maximal distance between supports on sparse infill sections." +msgstr "" +"Distância máxima entre as sustentações em seções preenchimento esparsas." + +#: src/libslic3r/PrintConfig.cpp:2225 +msgid "XY Size Compensation" +msgstr "Compensação de tamanho em XY" + +#: src/libslic3r/PrintConfig.cpp:2227 +msgid "" +"The object will be grown/shrunk in the XY plane by the configured value " +"(negative = inwards, positive = outwards). This might be useful for fine-" +"tuning hole sizes." +msgstr "" +"O objeto será aumentado/encolhido no plano XY pelo valor configurado " +"(negativo = para dentro, positivo = para fora). Isso pode ser útil para " +"ajustar os tamanhos dos furos." + +#: src/libslic3r/PrintConfig.cpp:2235 +msgid "Z offset" +msgstr "Compensamento do Z" + +#: src/libslic3r/PrintConfig.cpp:2236 +msgid "" +"This value will be added (or subtracted) from all the Z coordinates in the " +"output G-code. It is used to compensate for bad Z endstop position: for " +"example, if your endstop zero actually leaves the nozzle 0.3mm far from the " +"print bed, set this to -0.3 (or fix your endstop)." +msgstr "" +"Esse valor será adicionado (ou subtraído) de todas as coordenadas Z no G-" +"code de saída. Ele é usado para compensar a posição de final de curso Z " +"ruim: por exemplo, se o seu final de curso zero realmente deixa o bico 0.3 " +"mm longe da mesa de impressão, defina este para-0,3 (ou corrigir o seu final " +"de curso)." + +#: src/libslic3r/PrintConfig.cpp:2294 +msgid "Display width" +msgstr "Largura do display" + +#: src/libslic3r/PrintConfig.cpp:2295 +msgid "Width of the display" +msgstr "Largura do display" + +#: src/libslic3r/PrintConfig.cpp:2300 +msgid "Display height" +msgstr "Altura do display" + +#: src/libslic3r/PrintConfig.cpp:2301 +msgid "Height of the display" +msgstr "Altura do display" + +#: src/libslic3r/PrintConfig.cpp:2306 +msgid "Number of pixels in" +msgstr "Número de pixels em" + +#: src/libslic3r/PrintConfig.cpp:2308 +msgid "Number of pixels in X" +msgstr "Número de pixels em X" + +#: src/libslic3r/PrintConfig.cpp:2314 +msgid "Number of pixels in Y" +msgstr "Número de pixels em Y" + +#: src/libslic3r/PrintConfig.cpp:2319 +msgid "Display horizontal mirroring" +msgstr "Exibir espelhamento horizontal" + +#: src/libslic3r/PrintConfig.cpp:2320 +msgid "Mirror horizontally" +msgstr "Espelhar horizontalmente" + +#: src/libslic3r/PrintConfig.cpp:2321 +msgid "Enable horizontal mirroring of output images" +msgstr "Habilitar espelhamento horizontal de imagens de saída" + +#: src/libslic3r/PrintConfig.cpp:2326 +msgid "Display vertical mirroring" +msgstr "Exibir espelhamento vertical" + +#: src/libslic3r/PrintConfig.cpp:2327 +msgid "Mirror vertically" +msgstr "Espelharvertical" + +#: src/libslic3r/PrintConfig.cpp:2328 +msgid "Enable vertical mirroring of output images" +msgstr "Habilitar espelhamento vertical de imagens de saída" + +#: src/libslic3r/PrintConfig.cpp:2333 +msgid "Display orientation" +msgstr "Orientação do display" + +#: src/libslic3r/PrintConfig.cpp:2334 +msgid "" +"Set the actual LCD display orientation inside the SLA printer. Portrait mode " +"will flip the meaning of display width and height parameters and the output " +"images will be rotated by 90 degrees." +msgstr "" +"Defina a orientação real do visor LCD dentro da impressora SLA. O modo " +"retrato inverterá o significado dos parâmetros de largura e altura da tela e " +"as imagens de saída serão giradas por 90 graus." + +#: src/libslic3r/PrintConfig.cpp:2340 +msgid "Landscape" +msgstr "Paisagem" + +#: src/libslic3r/PrintConfig.cpp:2341 +msgid "Portrait" +msgstr "Retrato" + +#: src/libslic3r/PrintConfig.cpp:2346 +msgid "Fast" +msgstr "Rápido" + +#: src/libslic3r/PrintConfig.cpp:2347 +msgid "Fast tilt" +msgstr "Inclinação rápida" + +#: src/libslic3r/PrintConfig.cpp:2348 +msgid "Time of the fast tilt" +msgstr "Tempo da inclinação rápida" + +#: src/libslic3r/PrintConfig.cpp:2355 +msgid "Slow" +msgstr "Lento" + +#: src/libslic3r/PrintConfig.cpp:2356 +msgid "Slow tilt" +msgstr "Inclinação lenta" + +#: src/libslic3r/PrintConfig.cpp:2357 +msgid "Time of the slow tilt" +msgstr "Tempo da inclinação lenta" + +#: src/libslic3r/PrintConfig.cpp:2364 +msgid "Area fill" +msgstr "Preenchimento de área" + +#: src/libslic3r/PrintConfig.cpp:2365 +msgid "" +"The percentage of the bed area. \n" +"If the print area exceeds the specified value, \n" +"then a slow tilt will be used, otherwise - a fast tilt" +msgstr "" +"A porcentagem da área de mesa. \n" +"Se a área de impressão exceder o valor especificado, \n" +"em seguida, uma inclinação lenta será usada, caso contrário-uma inclinação " +"rápida" + +#: src/libslic3r/PrintConfig.cpp:2372 src/libslic3r/PrintConfig.cpp:2373 +#: src/libslic3r/PrintConfig.cpp:2374 +msgid "Printer scaling correction" +msgstr "Correção de dimensionamento da impressora" + +#: src/libslic3r/PrintConfig.cpp:2380 src/libslic3r/PrintConfig.cpp:2381 +msgid "Printer absolute correction" +msgstr "Correção absoluta da impressora" + +#: src/libslic3r/PrintConfig.cpp:2382 +msgid "" +"Will inflate or deflate the sliced 2D polygons according to the sign of the " +"correction." +msgstr "" +"Irá inflar ou esvaziar os polígonos 2D cortados de acordo com o sinal da " +"correção." + +#: src/libslic3r/PrintConfig.cpp:2388 src/libslic3r/PrintConfig.cpp:2389 +msgid "Printer gamma correction" +msgstr "Correção de gama de impressora" + +#: src/libslic3r/PrintConfig.cpp:2390 +msgid "" +"This will apply a gamma correction to the rasterized 2D polygons. A gamma " +"value of zero means thresholding with the threshold in the middle. This " +"behaviour eliminates antialiasing without losing holes in polygons." +msgstr "" +"Isso aplicará uma correção de gama para os polígonos 2D rasterizados. Um " +"valor gama de zero significa limiarização com o limiar no meio. Este " +"comportamento elimina suavização sem perder buracos em polígonos." + +#: src/libslic3r/PrintConfig.cpp:2401 src/libslic3r/PrintConfig.cpp:2402 +msgid "Initial layer height" +msgstr "Altura da camada inicial" + +#: src/libslic3r/PrintConfig.cpp:2408 +msgid "Faded layers" +msgstr "Camadas desbotadas" + +#: src/libslic3r/PrintConfig.cpp:2409 +msgid "" +"Number of the layers needed for the exposure time fade from initial exposure " +"time to the exposure time" +msgstr "" +"Número de camadas necessárias para o tempo de exposição desvanecer-se do " +"tempo de exposição inicial ao tempo de exposição" + +#: src/libslic3r/PrintConfig.cpp:2416 src/libslic3r/PrintConfig.cpp:2417 +msgid "Minimum exposure time" +msgstr "Tempo mínimo de exposição" + +#: src/libslic3r/PrintConfig.cpp:2424 src/libslic3r/PrintConfig.cpp:2425 +msgid "Maximum exposure time" +msgstr "Tempo máximo de exposição" + +#: src/libslic3r/PrintConfig.cpp:2432 src/libslic3r/PrintConfig.cpp:2433 +msgid "Exposure time" +msgstr "Tempo de exposição" + +#: src/libslic3r/PrintConfig.cpp:2439 src/libslic3r/PrintConfig.cpp:2440 +msgid "Minimum initial exposure time" +msgstr "Tempo inicial mínimo de exposição" + +#: src/libslic3r/PrintConfig.cpp:2447 src/libslic3r/PrintConfig.cpp:2448 +msgid "Maximum initial exposure time" +msgstr "Tempo inicial máximo de exposição" + +#: src/libslic3r/PrintConfig.cpp:2455 src/libslic3r/PrintConfig.cpp:2456 +msgid "Initial exposure time" +msgstr "Tempo inicial mínimo de exposição" + +#: src/libslic3r/PrintConfig.cpp:2462 src/libslic3r/PrintConfig.cpp:2463 +msgid "Correction for expansion" +msgstr "Correção para expansão" + +#: src/libslic3r/PrintConfig.cpp:2469 +msgid "SLA print material notes" +msgstr "Notas de material de impressão de SLA" + +#: src/libslic3r/PrintConfig.cpp:2470 +msgid "You can put your notes regarding the SLA print material here." +msgstr "" +"Você pode colocar suas anotações sobre o material de impressão de SLA aqui." + +#: src/libslic3r/PrintConfig.cpp:2478 src/libslic3r/PrintConfig.cpp:2489 +msgid "Default SLA material profile" +msgstr "Perfil de material de SLA padrão" + +#: src/libslic3r/PrintConfig.cpp:2500 +msgid "Generate supports" +msgstr "Gerar suportes" + +#: src/libslic3r/PrintConfig.cpp:2502 +msgid "Generate supports for the models" +msgstr "Gere suportes para os modelos" + +#: src/libslic3r/PrintConfig.cpp:2507 +msgid "Support head front diameter" +msgstr "Diâmetro dianteiro principal da sustentação" + +#: src/libslic3r/PrintConfig.cpp:2509 +msgid "Diameter of the pointing side of the head" +msgstr "Diâmetro do lado apontando da cabeça" + +#: src/libslic3r/PrintConfig.cpp:2516 +msgid "Support head penetration" +msgstr "Suporte de penetração da cabeça" + +#: src/libslic3r/PrintConfig.cpp:2518 +msgid "How much the pinhead has to penetrate the model surface" +msgstr "Quanto a cabeça de alfinete tem de penetrar na superfície do modelo" + +#: src/libslic3r/PrintConfig.cpp:2525 +msgid "Support head width" +msgstr "Largura da cabeça de suporte" + +#: src/libslic3r/PrintConfig.cpp:2527 +msgid "Width from the back sphere center to the front sphere center" +msgstr "Largura do centro da esfera traseira ao centro da esfera dianteira" + +#: src/libslic3r/PrintConfig.cpp:2535 +msgid "Support pillar diameter" +msgstr "Diâmetro do pilar do suporte" + +#: src/libslic3r/PrintConfig.cpp:2537 +msgid "Diameter in mm of the support pillars" +msgstr "Diâmetro em mm dos pilares de suporte" + +#: src/libslic3r/PrintConfig.cpp:2545 +msgid "Support pillar connection mode" +msgstr "Modalidade da conexão da coluna da sustentação" + +#: src/libslic3r/PrintConfig.cpp:2546 +msgid "" +"Controls the bridge type between two neighboring pillars. Can be zig-zag, " +"cross (double zig-zag) or dynamic which will automatically switch between " +"the first two depending on the distance of the two pillars." +msgstr "" +"Controla o tipo de ponte entre dois pilares vizinhos. Pode ser zig-zag, Cruz " +"(zig-zag dobro) ou dinâmico que comutará automaticamente entre os primeiros " +"dois dependendo da distância dos dois pilares." + +#: src/libslic3r/PrintConfig.cpp:2554 +msgid "Zig-Zag" +msgstr "Zig-Zag" + +#: src/libslic3r/PrintConfig.cpp:2555 +msgid "Cross" +msgstr "Cruz" + +#: src/libslic3r/PrintConfig.cpp:2556 +msgid "Dynamic" +msgstr "Dinâmico" + +#: src/libslic3r/PrintConfig.cpp:2568 +msgid "Pillar widening factor" +msgstr "Fator de alargamento da coluna" + +#: src/libslic3r/PrintConfig.cpp:2570 +msgid "" +"Merging bridges or pillars into another pillars can increase the radius. " +"Zero means no increase, one means full increase." +msgstr "" +"Mesclar pontes ou pilares em outros pilares pode aumentar o raio. Zero " +"significa que não há aumento, um significa aumento total." + +#: src/libslic3r/PrintConfig.cpp:2579 +msgid "Support base diameter" +msgstr "Diâmetro base do suporte" + +#: src/libslic3r/PrintConfig.cpp:2581 +msgid "Diameter in mm of the pillar base" +msgstr "Diâmetro em mm da base do pilar" + +#: src/libslic3r/PrintConfig.cpp:2589 +msgid "Support base height" +msgstr "Altura base do suporte" + +#: src/libslic3r/PrintConfig.cpp:2591 +msgid "The height of the pillar base cone" +msgstr "A altura do cone da base da coluna" + +#: src/libslic3r/PrintConfig.cpp:2598 +msgid "Support base safety distance" +msgstr "Distância da segurança da base da sustentação" + +#: src/libslic3r/PrintConfig.cpp:2601 +msgid "" +"The minimum distance of the pillar base from the model in mm. Makes sense in " +"zero elevation mode where a gap according to this parameter is inserted " +"between the model and the pad." +msgstr "" +"A distância mínima da base do pilar do modelo em mm. faz sentido no modo de " +"elevação zero, onde uma lacuna de acordo com este parâmetro é inserida entre " +"o modelo e o pad." + +#: src/libslic3r/PrintConfig.cpp:2611 +msgid "Critical angle" +msgstr "Ângulo crítico" + +#: src/libslic3r/PrintConfig.cpp:2613 +msgid "The default angle for connecting support sticks and junctions." +msgstr "O ângulo padrão para conectar suportes e junções." + +#: src/libslic3r/PrintConfig.cpp:2621 +msgid "Max bridge length" +msgstr "Comprimento máximo da ponte" + +#: src/libslic3r/PrintConfig.cpp:2623 +msgid "The max length of a bridge" +msgstr "O comprimento máximo de uma ponte" + +#: src/libslic3r/PrintConfig.cpp:2630 +msgid "Max pillar linking distance" +msgstr "Distância máxima de conexão entre pilares" + +#: src/libslic3r/PrintConfig.cpp:2632 +msgid "" +"The max distance of two pillars to get linked with each other. A zero value " +"will prohibit pillar cascading." +msgstr "" +"A distância máxima de dois pilares para ficar ligado uns com os outros. Um " +"valor zero irá proibir o pilar em cascata." + +#: src/libslic3r/PrintConfig.cpp:2640 +msgid "Object elevation" +msgstr "Elevação do objeto" + +#: src/libslic3r/PrintConfig.cpp:2642 +msgid "" +"How much the supports should lift up the supported object. If \"Pad around " +"object\" is enabled, this value is ignored." +msgstr "" +"Quanto os suportes devem levantar o objecto suportado. Se \"pad em torno do " +"objeto\" estiver habilitado, esse valor será ignorado." + +#: src/libslic3r/PrintConfig.cpp:2653 +msgid "This is a relative measure of support points density." +msgstr "Esta é uma medida relativa de densidade de pontos de suporte." + +#: src/libslic3r/PrintConfig.cpp:2659 +msgid "Minimal distance of the support points" +msgstr "Distância mínima dos pontos de suporte" + +#: src/libslic3r/PrintConfig.cpp:2661 +msgid "No support points will be placed closer than this threshold." +msgstr "Nenhum ponto de apoio será colocado mais perto do que este limiar." + +#: src/libslic3r/PrintConfig.cpp:2667 +msgid "Use pad" +msgstr "Use pad" + +#: src/libslic3r/PrintConfig.cpp:2669 +msgid "Add a pad underneath the supported model" +msgstr "Adicionar um pad por baixo do modelo suportado" + +#: src/libslic3r/PrintConfig.cpp:2674 +msgid "Pad wall thickness" +msgstr "Espessura da parede do pad" + +#: src/libslic3r/PrintConfig.cpp:2676 +msgid "The thickness of the pad and its optional cavity walls." +msgstr "A espessura da pad e suas paredes de cavidade opcionais." + +#: src/libslic3r/PrintConfig.cpp:2684 +msgid "Pad wall height" +msgstr "Altura da parede do pad" + +#: src/libslic3r/PrintConfig.cpp:2685 +msgid "" +"Defines the pad cavity depth. Set to zero to disable the cavity. Be careful " +"when enabling this feature, as some resins may produce an extreme suction " +"effect inside the cavity, which makes peeling the print off the vat foil " +"difficult." +msgstr "" +"Define a profundidade da cavidade da pad. Defina como zero para desabilitar " +"a cavidade. Tenha cuidado ao ativar este recurso, como algumas resinas podem " +"produzir um efeito de sucção extrema dentro da cavidade, o que torna a " +"descascar a impressão fora da folha de IVA difícil." + +#: src/libslic3r/PrintConfig.cpp:2698 +msgid "Max merge distance" +msgstr "Distância máxima da fusão" + +#: src/libslic3r/PrintConfig.cpp:2700 +msgid "" +"Some objects can get along with a few smaller pads instead of a single big " +"one. This parameter defines how far the center of two smaller pads should " +"be. If theyare closer, they will get merged into one pad." +msgstr "" +"Alguns objetos podem se dar bem com algumas pads menores em vez de um único " +"grande. Este parâmetro define até que ponto o centro de duas pads menores " +"deve ser. Se eles estão mais perto, eles vão se fundir em uma pad." + +#: src/libslic3r/PrintConfig.cpp:2720 +msgid "Pad wall slope" +msgstr "Inclinação da parede da pad" + +#: src/libslic3r/PrintConfig.cpp:2722 +msgid "" +"The slope of the pad wall relative to the bed plane. 90 degrees means " +"straight walls." +msgstr "" +"A inclinação da parede da pad em relação ao plano da mesa. 90 graus " +"significa paredes retas." + +#: src/libslic3r/PrintConfig.cpp:2731 +msgid "Pad around object" +msgstr "Pad em torno do objeto" + +#: src/libslic3r/PrintConfig.cpp:2733 +msgid "Create pad around object and ignore the support elevation" +msgstr "Criar pad ao redor do objeto e ignorar a elevação de suporte" + +#: src/libslic3r/PrintConfig.cpp:2738 +msgid "Pad object gap" +msgstr "Vão entre o pad e o objeto" + +#: src/libslic3r/PrintConfig.cpp:2740 +msgid "" +"The gap between the object bottom and the generated pad in zero elevation " +"mode." +msgstr "" +"A lacuna entre a parte inferior do objeto e o pad gerado no modo de elevação " +"zero." + +#: src/libslic3r/PrintConfig.cpp:2749 +msgid "Pad object connector stride" +msgstr "Inserir pad entre o objeto" + +#: src/libslic3r/PrintConfig.cpp:2751 +msgid "" +"Distance between two connector sticks which connect the object and the " +"generated pad." +msgstr "" +"Distância entre duas varas do conector que conectam o objeto e a pad gerada." + +#: src/libslic3r/PrintConfig.cpp:2758 +msgid "Pad object connector width" +msgstr "Largura do conector do objeto pad" + +#: src/libslic3r/PrintConfig.cpp:2760 +msgid "" +"Width of the connector sticks which connect the object and the generated pad." +msgstr "Largura das varas do conector que conectam o objeto e a pad gerada." + +#: src/libslic3r/PrintConfig.cpp:2767 +msgid "Pad object connector penetration" +msgstr "Pad objeto conector de penetração" + +#: src/libslic3r/PrintConfig.cpp:2770 +msgid "How much should the tiny connectors penetrate into the model body." +msgstr "Quanto deve os conectores minúsculos penetrar no corpo do modelo." + +#: src/libslic3r/PrintConfig.cpp:3130 +msgid "Export OBJ" +msgstr "Exportar OBJ" + +#: src/libslic3r/PrintConfig.cpp:3131 +msgid "Export the model(s) as OBJ." +msgstr "Exportar modelo(s) como OBJ." + +#: src/libslic3r/PrintConfig.cpp:3142 +msgid "Export SLA" +msgstr "Exportar SLA" + +#: src/libslic3r/PrintConfig.cpp:3143 +msgid "Slice the model and export SLA printing layers as PNG." +msgstr "Fatiar o modelo e exportar as camadas de impressão SLA como PNG." + +#: src/libslic3r/PrintConfig.cpp:3148 +msgid "Export 3MF" +msgstr "Exportar 3MF" + +#: src/libslic3r/PrintConfig.cpp:3149 +msgid "Export the model(s) as 3MF." +msgstr "Exportar modelo(s) como 3MF." + +#: src/libslic3r/PrintConfig.cpp:3153 +msgid "Export AMF" +msgstr "Exportar AMF" + +#: src/libslic3r/PrintConfig.cpp:3154 +msgid "Export the model(s) as AMF." +msgstr "Exportar modelo(s) como AMF." + +#: src/libslic3r/PrintConfig.cpp:3158 +msgid "Export STL" +msgstr "Exportar STL" + +#: src/libslic3r/PrintConfig.cpp:3159 +msgid "Export the model(s) as STL." +msgstr "Exportar modelo(s) como STL." + +#: src/libslic3r/PrintConfig.cpp:3164 +msgid "Slice the model and export toolpaths as G-code." +msgstr "Fatiar o modelo e exportar o percurso da ferramenta como G-code." + +#: src/libslic3r/PrintConfig.cpp:3169 +msgid "Slice" +msgstr "Fatiar" + +#: src/libslic3r/PrintConfig.cpp:3170 +msgid "" +"Slice the model as FFF or SLA based on the printer_technology configuration " +"value." +msgstr "" +"Divida o modelo como FFF ou SLA com base no valor de config. " +"printer_technology." + +#: src/libslic3r/PrintConfig.cpp:3175 +msgid "Help" +msgstr "Ajuda" + +#: src/libslic3r/PrintConfig.cpp:3176 +msgid "Show this help." +msgstr "Mostrar esta ajuda." + +#: src/libslic3r/PrintConfig.cpp:3181 +msgid "Help (FFF options)" +msgstr "Ajuda (opções FDM)" + +#: src/libslic3r/PrintConfig.cpp:3182 +msgid "Show the full list of print/G-code configuration options." +msgstr "Mostre a lista completa de opções de config. do Print/G-code." + +#: src/libslic3r/PrintConfig.cpp:3186 +msgid "Help (SLA options)" +msgstr "Ajuda (opções SLA)" + +#: src/libslic3r/PrintConfig.cpp:3187 +msgid "Show the full list of SLA print configuration options." +msgstr "Mostrar a lista completa de opções de config. de impressão de SLA." + +#: src/libslic3r/PrintConfig.cpp:3191 +msgid "Output Model Info" +msgstr "Informações do modelo de saída" + +#: src/libslic3r/PrintConfig.cpp:3192 +msgid "Write information about the model to the console." +msgstr "Escreva informações sobre o modelo para o console." + +#: src/libslic3r/PrintConfig.cpp:3196 +msgid "Save config file" +msgstr "Salvar arquivo de config." + +#: src/libslic3r/PrintConfig.cpp:3197 +msgid "Save configuration to the specified file." +msgstr "Salvar config. para o arquivo específico." + +#: src/libslic3r/PrintConfig.cpp:3207 +msgid "Align XY" +msgstr "Alinhar XY" + +#: src/libslic3r/PrintConfig.cpp:3208 +msgid "Align the model to the given point." +msgstr "Alinhar modelo de acordo com o ponto inserido." + +#: src/libslic3r/PrintConfig.cpp:3213 +msgid "Cut model at the given Z." +msgstr "Cortar modelo ao Z fornecido." + +#: src/libslic3r/PrintConfig.cpp:3234 +msgid "Center" +msgstr "Centralizar" + +#: src/libslic3r/PrintConfig.cpp:3235 +msgid "Center the print around the given center." +msgstr "Centralizar a impressão de acordo com o centro informado." + +#: src/libslic3r/PrintConfig.cpp:3239 +msgid "Don't arrange" +msgstr "Não organizar" + +#: src/libslic3r/PrintConfig.cpp:3240 +msgid "" +"Do not rearrange the given models before merging and keep their original XY " +"coordinates." +msgstr "" +"Não reorganize os modelos fornecidos antes de Mesclar e manter suas " +"coordenadas XY originais." + +#: src/libslic3r/PrintConfig.cpp:3243 +msgid "Duplicate" +msgstr "Duplicar" + +#: src/libslic3r/PrintConfig.cpp:3244 +msgid "Multiply copies by this factor." +msgstr "Multiplicar cópias por esse fator." + +#: src/libslic3r/PrintConfig.cpp:3248 +msgid "Duplicate by grid" +msgstr "Duplicar por grade" + +#: src/libslic3r/PrintConfig.cpp:3249 +msgid "Multiply copies by creating a grid." +msgstr "Multiplique cópias criando uma grade." + +#: src/libslic3r/PrintConfig.cpp:3252 +msgid "Merge" +msgstr "Mesclar" + +#: src/libslic3r/PrintConfig.cpp:3253 +msgid "" +"Arrange the supplied models in a plate and merge them in a single model in " +"order to perform actions once." +msgstr "" +"Organize os modelos fornecidos em uma placa e junte-os em um único modelo, a " +"fim de executar ações uma só vez." + +#: src/libslic3r/PrintConfig.cpp:3258 +msgid "" +"Try to repair any non-manifold meshes (this option is implicitly added " +"whenever we need to slice the model to perform the requested action)." +msgstr "" +"Tente reparar qualquer malhas não multiplicadas (essa opção é implicitamente " +"adicionada sempre que precisamos cortar o modelo para executar a ação " +"solicitada)." + +#: src/libslic3r/PrintConfig.cpp:3262 +msgid "Rotation angle around the Z axis in degrees." +msgstr "Ângulo de rotação ao redor do eixo Zem graus." + +#: src/libslic3r/PrintConfig.cpp:3266 +msgid "Rotate around X" +msgstr "Rotacionar no X" + +#: src/libslic3r/PrintConfig.cpp:3267 +msgid "Rotation angle around the X axis in degrees." +msgstr "Ângulo de rotação ao redor do eixo X em graus." + +#: src/libslic3r/PrintConfig.cpp:3271 +msgid "Rotate around Y" +msgstr "Rotacionar no Y" + +#: src/libslic3r/PrintConfig.cpp:3272 +msgid "Rotation angle around the Y axis in degrees." +msgstr "Ângulo de rotação ao redor do eixo Y em graus." + +#: src/libslic3r/PrintConfig.cpp:3277 +msgid "Scaling factor or percentage." +msgstr "Escalando fator ou porcentagem." + +#: src/libslic3r/PrintConfig.cpp:3282 +msgid "" +"Detect unconnected parts in the given model(s) and split them into separate " +"objects." +msgstr "" +"Detecte peças não conectadas em um determinado modelo (s) e divida-as em " +"objetos separados." + +#: src/libslic3r/PrintConfig.cpp:3285 +msgid "Scale to Fit" +msgstr "Dimensionar para caber" + +#: src/libslic3r/PrintConfig.cpp:3286 +msgid "Scale to fit the given volume." +msgstr "Escalar para se adequar ao volume informado." + +#: src/libslic3r/PrintConfig.cpp:3295 +msgid "Ignore non-existent config files" +msgstr "Ignorar arquivos de config. não existentes" + +#: src/libslic3r/PrintConfig.cpp:3296 +msgid "Do not fail if a file supplied to --load does not exist." +msgstr "Não falhe se um arquivo fornecido para--carregamento não existe." + +#: src/libslic3r/PrintConfig.cpp:3299 +msgid "Load config file" +msgstr "Carregar arquivo de config." + +#: src/libslic3r/PrintConfig.cpp:3300 +msgid "" +"Load configuration from the specified file. It can be used more than once to " +"load options from multiple files." +msgstr "" +"Carregar a config. do arquivo especificado. Ele pode ser usado mais de uma " +"vez para carregar opções de vários arquivos." + +#: src/libslic3r/PrintConfig.cpp:3303 +msgid "Output File" +msgstr "Arquivo de saída" + +#: src/libslic3r/PrintConfig.cpp:3304 +msgid "" +"The file where the output will be written (if not specified, it will be " +"based on the input file)." +msgstr "" +"O arquivo onde a saída será gravada (se não for especificado, ele será " +"baseado no arquivo de entrada)." + +#: src/libslic3r/PrintConfig.cpp:3314 +msgid "Data directory" +msgstr "Diretório de dados" + +#: src/libslic3r/PrintConfig.cpp:3315 +msgid "" +"Load and store settings at the given directory. This is useful for " +"maintaining different profiles or including configurations from a network " +"storage." +msgstr "" +"Carregar e armazenar as config. no diretório especificado. Isso é útil para " +"manter perfis diferentes ou incluir config. de um armazenamento de rede." + +#: src/libslic3r/PrintConfig.cpp:3318 +msgid "Logging level" +msgstr "Nível de registro" + +#: src/libslic3r/PrintConfig.cpp:3319 +msgid "" +"Messages with severity lower or eqal to the loglevel will be printed out. 0:" +"trace, 1:debug, 2:info, 3:warning, 4:error, 5:fatal" +msgstr "" +"Mensagens com severidade menor ou igual para o LogLevel serão impressos. 0: " +"Trace, 1: debug, 2: info, 3: aviso, 4: erro, 5: fatal" + +#: src/libslic3r/PrintConfig.cpp:3324 +msgid "Render with a software renderer" +msgstr "Renderizar com um software renderizador" + +#: src/libslic3r/PrintConfig.cpp:3325 +msgid "" +"Render with a software renderer. The bundled MESA software renderer is " +"loaded instead of the default OpenGL driver." +msgstr "" +"Renderizar com um software renderizador. O renderizador de software MESA " +"empacotado é carregado em vez do driver OpenGL padrão." + +#: src/libslic3r/PrintObject.cpp:110 +msgid "Processing triangulated mesh" +msgstr "Processando malha triangulada" + +#: src/libslic3r/PrintObject.cpp:141 +msgid "Generating perimeters" +msgstr "Gerando perímetros" + +#: src/libslic3r/PrintObject.cpp:251 +msgid "Preparing infill" +msgstr "Preparando o preenchimento" + +#: src/libslic3r/PrintObject.cpp:391 +msgid "Generating support material" +msgstr "Gerando material de suporte" + +#: src/libslic3r/GCode/PreviewData.cpp:160 +msgid "Mixed" +msgstr "Misto" + +#: src/libslic3r/GCode/PreviewData.cpp:380 +msgid "Height (mm)" +msgstr "Altura (mm)" + +#: src/libslic3r/GCode/PreviewData.cpp:382 +msgid "Width (mm)" +msgstr "Espessura (mm)" + +#: src/libslic3r/GCode/PreviewData.cpp:384 +msgid "Speed (mm/s)" +msgstr "Velocidade (mm/s)" + +#: src/libslic3r/GCode/PreviewData.cpp:386 +msgid "Volumetric flow rate (mm3/s)" +msgstr "Fluxo volumétrico (mm3/s)" + +#: src/libslic3r/GCode/PreviewData.cpp:477 +msgid "Default print color" +msgstr "Cor de impressão padrão" + +#: src/libslic3r/GCode/PreviewData.cpp:484 +#, c-format +msgid "up to %.2f mm" +msgstr "até %.2f mm" + +#: src/libslic3r/GCode/PreviewData.cpp:488 +#, c-format +msgid "above %.2f mm" +msgstr "acima de %.2f mm" + +#: src/libslic3r/GCode/PreviewData.cpp:493 +#, c-format +msgid "%.2f - %.2f mm" +msgstr "%.2f - %.2f mm" From 6fcd51d5be79783037414f9ea57bbe190b82ba6c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 22 Nov 2019 16:08:25 +0100 Subject: [PATCH 69/73] Fix of "Slicer crashes when slicing for flexible filament" #2988 --- src/slic3r/GUI/GLCanvas3D.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 3f10f9bd2..693204435 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2296,12 +2296,12 @@ static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume& static void load_gcode_retractions(const GCodePreviewData::Retraction& retractions, GLCanvas3D::GCodePreviewVolumeIndex::EType extrusion_type, GLVolumeCollection &volumes, GLCanvas3D::GCodePreviewVolumeIndex &volume_index, bool gl_initialized) { - volume_index.first_volumes.emplace_back(extrusion_type, 0, (unsigned int)volumes.volumes.size()); - // nothing to render, return if (retractions.positions.empty()) return; + volume_index.first_volumes.emplace_back(extrusion_type, 0, (unsigned int)volumes.volumes.size()); + GLVolume *volume = volumes.new_nontoolpath_volume(retractions.color.rgba, VERTEX_BUFFER_RESERVE_SIZE); GCodePreviewData::Retraction::PositionsList copy(retractions.positions); @@ -2367,6 +2367,9 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const ++ idx_volume_index_src; idx_volume_of_this_type_last = (idx_volume_index_src + 1 == m_gcode_preview_volume_index.first_volumes.size()) ? m_volumes.volumes.size() : m_gcode_preview_volume_index.first_volumes[idx_volume_index_src + 1].id; idx_volume_of_this_type_first_new = idx_volume_dst; + if (idx_volume_src == idx_volume_of_this_type_last) + // Empty sequence of volumes for the current index item. + continue; } if (! m_volumes.volumes[idx_volume_src]->print_zs.empty()) m_volumes.volumes[idx_volume_dst ++] = m_volumes.volumes[idx_volume_src]; From 9c4dc80057dd311b2fee4c639918f0cf0b5642a1 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 22 Nov 2019 18:22:44 +0100 Subject: [PATCH 70/73] Fix of the new gyroid infill path planning. Fixes #3226 --- src/libslic3r/Fill/FillBase.cpp | 9 +++- src/libslic3r/Geometry.hpp | 73 +++++++++++++++++++++++++++++++++ src/libslic3r/Line.cpp | 11 +++++ src/libslic3r/Line.hpp | 3 ++ 4 files changed, 94 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 88eba9a51..fa7dd2851 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -2,6 +2,7 @@ #include "../ClipperUtils.hpp" #include "../EdgeGrid.hpp" +#include "../Geometry.hpp" #include "../Surface.hpp" #include "../PrintConfig.hpp" #include "../libslic3r.h" @@ -777,6 +778,8 @@ void mark_boundary_segments_touching_infill( const Vec2d *pt2; } visitor(grid, boundary, boundary_data, distance_colliding * distance_colliding); + BoundingBoxf bboxf(boundary_bbox.min.cast(), boundary_bbox.max.cast()); + bboxf.offset(- SCALED_EPSILON); for (const Polyline &polyline : infill) { // Clip the infill polyline by the Eucledian distance along the polyline. SegmentPoint start_point = clip_start_segment_and_point(polyline.points, clip_distance); @@ -814,10 +817,12 @@ void mark_boundary_segments_touching_infill( Vec2d vperp(-v.y(), v.x()); Vec2d a = pt1 - v - vperp; Vec2d b = pt1 + v - vperp; - grid.visit_cells_intersecting_line(a.cast(), b.cast(), visitor); + if (Geometry::liang_barsky_line_clipping(a, b, bboxf)) + grid.visit_cells_intersecting_line(a.cast(), b.cast(), visitor); a = pt1 - v + vperp; b = pt1 + v + vperp; - grid.visit_cells_intersecting_line(a.cast(), b.cast(), visitor); + if (Geometry::liang_barsky_line_clipping(a, b, bboxf)) + grid.visit_cells_intersecting_line(a.cast(), b.cast(), visitor); #endif } } diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index d996658f2..283d1edb0 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -137,6 +137,79 @@ inline bool segments_intersect( segments_could_intersect(jp1, jp2, ip1, ip2) <= 0; } +// Based on Liang-Barsky function by Daniel White @ http://www.skytopia.com/project/articles/compsci/clipping.html +template +bool liang_barsky_line_clipping( + // Start and end points of the source line, result will be stored there as well. + Eigen::Matrix &x0, + Eigen::Matrix &x1, + // Bounding box to clip with. + const BoundingBoxBase> &bbox) +{ + Eigen::Matrix v = x1 - x0; + double t0 = 0.0; + double t1 = 1.0; + + // Traverse through left, right, bottom, top edges. + for (int edge = 0; edge < 4; ++ edge) + { + double p, q; + switch (edge) { + case 0: p = - v.x(); q = - bbox.min.x() + x0.x(); break; + case 1: p = v.x(); q = bbox.max.x() - x0.x(); break; + case 2: p = - v.y(); q = - bbox.min.y() + x0.y(); break; + default: p = v.y(); q = bbox.max.y() - x0.y(); break; + } + + if (p == 0) { + if (q < 0) + // Line parallel to the bounding box edge is fully outside of the bounding box. + return false; + // else don't clip + } else { + double r = q / p; + if (p < 0) { + if (r > t1) + // Fully clipped. + return false; + if (r > t0) + // Partially clipped. + t0 = r; + } else { + assert(p > 0); + if (r < t0) + // Fully clipped. + return false; + if (r < t1) + // Partially clipped. + t1 = r; + } + } + } + + // Clipped successfully. + x1 = x0 + t1 * v; + x0 += t0 * v; + return true; +} + +// Based on Liang-Barsky function by Daniel White @ http://www.skytopia.com/project/articles/compsci/clipping.html +template +bool liang_barsky_line_clipping( + // Start and end points of the source line. + const Eigen::Matrix &x0src, + const Eigen::Matrix &x1src, + // Bounding box to clip with. + const BoundingBoxBase> &bbox, + // Start and end points of the clipped line. + Eigen::Matrix &x0clip, + Eigen::Matrix &x1clip) +{ + x0clip = x0src; + x1clip = x1src; + return liang_barsky_line_clipping(x0clip, x1clip, bbox); +} + Pointf3s convex_hull(Pointf3s points); Polygon convex_hull(Points points); Polygon convex_hull(const Polygons &polygons); diff --git a/src/libslic3r/Line.cpp b/src/libslic3r/Line.cpp index e5f7b8fa9..05cbfee4e 100644 --- a/src/libslic3r/Line.cpp +++ b/src/libslic3r/Line.cpp @@ -107,6 +107,17 @@ bool Line::intersection(const Line &l2, Point *intersection) const return false; // not intersecting } +bool Line::clip_with_bbox(const BoundingBox &bbox) +{ + Vec2d x0clip, x1clip; + bool result = Geometry::liang_barsky_line_clipping(this->a.cast(), this->b.cast(), BoundingBoxf(bbox.min.cast(), bbox.max.cast()), x0clip, x1clip); + if (result) { + this->a = x0clip.cast(); + this->b = x1clip.cast(); + } + return result; +} + Vec3d Linef3::intersect_plane(double z) const { auto v = (this->b - this->a).cast(); diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index 559ca946a..06809c02a 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -6,6 +6,7 @@ namespace Slic3r { +class BoundingBox; class Line; class Line3; class Linef3; @@ -43,6 +44,8 @@ public: Vector normal() const { return Vector((this->b(1) - this->a(1)), -(this->b(0) - this->a(0))); } bool intersection(const Line& line, Point* intersection) const; double ccw(const Point& point) const { return point.ccw(*this); } + // Clip a line with a bounding box. Returns false if the line is completely outside of the bounding box. + bool clip_with_bbox(const BoundingBox &bbox); static double distance_to_squared(const Point &point, const Point &a, const Point &b); static double distance_to(const Point &point, const Point &a, const Point &b) { return sqrt(distance_to_squared(point, a, b)); } From 2b17e81f133dbc2aeb22d99d857b6a1d136ea247 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 22 Nov 2019 19:09:39 +0100 Subject: [PATCH 71/73] If of the previous commit: Set the MutablePriorityQueue indices to size_t(-1) when removed from the queue. --- src/libslic3r/MotionPlanner.cpp | 2 +- src/libslic3r/MutablePriorityQueue.hpp | 68 +++++++++++++++----------- src/libslic3r/ShortestPath.cpp | 6 +-- 3 files changed, 44 insertions(+), 32 deletions(-) diff --git a/src/libslic3r/MotionPlanner.cpp b/src/libslic3r/MotionPlanner.cpp index 42bc6c2f1..45a80671c 100644 --- a/src/libslic3r/MotionPlanner.cpp +++ b/src/libslic3r/MotionPlanner.cpp @@ -319,7 +319,7 @@ Polyline MotionPlannerGraph::shortest_path(size_t node_start, size_t node_end) c std::vector map_node_to_queue_id(m_adjacency_list.size(), size_t(-1)); distance[node_start] = 0.; - auto queue = make_mutable_priority_queue( + auto queue = make_mutable_priority_queue( [&map_node_to_queue_id](const node_t node, size_t idx) { map_node_to_queue_id[node] = idx; }, [&distance](const node_t node1, const node_t node2) { return distance[node1] < distance[node2]; }); queue.reserve(m_adjacency_list.size()); diff --git a/src/libslic3r/MutablePriorityQueue.hpp b/src/libslic3r/MutablePriorityQueue.hpp index da469b7ba..b20bf60ea 100644 --- a/src/libslic3r/MutablePriorityQueue.hpp +++ b/src/libslic3r/MutablePriorityQueue.hpp @@ -3,7 +3,7 @@ #include -template +template class MutablePriorityQueue { public: @@ -42,26 +42,30 @@ private: LessPredicate m_less_predicate; }; -template -MutablePriorityQueue make_mutable_priority_queue(IndexSetter &&index_setter, LessPredicate &&less_predicate) +template +MutablePriorityQueue make_mutable_priority_queue(IndexSetter &&index_setter, LessPredicate &&less_predicate) { - return MutablePriorityQueue( + return MutablePriorityQueue( std::forward(index_setter), std::forward(less_predicate)); } -template -inline void MutablePriorityQueue::clear() +template +inline void MutablePriorityQueue::clear() { -#ifndef NDEBUG - for (size_t idx = 0; idx < m_heap.size(); ++ idx) - // Mark as removed from the queue. - m_index_setter(m_heap[idx], std::numeric_limits::max()); +#ifdef NDEBUG + // Only mark as removed from the queue in release mode, if configured so. + if (ResetIndexWhenRemoved) #endif /* NDEBUG */ + { + for (size_t idx = 0; idx < m_heap.size(); ++ idx) + // Mark as removed from the queue. + m_index_setter(m_heap[idx], std::numeric_limits::max()); + } m_heap.clear(); } -template -inline void MutablePriorityQueue::push(const T &item) +template +inline void MutablePriorityQueue::push(const T &item) { size_t idx = m_heap.size(); m_heap.emplace_back(item); @@ -69,8 +73,8 @@ inline void MutablePriorityQueue::push(const T &i update_heap_up(0, idx); } -template -inline void MutablePriorityQueue::push(T &&item) +template +inline void MutablePriorityQueue::push(T &&item) { size_t idx = m_heap.size(); m_heap.emplace_back(std::move(item)); @@ -78,14 +82,18 @@ inline void MutablePriorityQueue::push(T &&item) update_heap_up(0, idx); } -template -inline void MutablePriorityQueue::pop() +template +inline void MutablePriorityQueue::pop() { assert(! m_heap.empty()); -#ifndef NDEBUG - // Mark as removed from the queue. - m_index_setter(m_heap.front(), std::numeric_limits::max()); +#ifdef NDEBUG + // Only mark as removed from the queue in release mode, if configured so. + if (ResetIndexWhenRemoved) #endif /* NDEBUG */ + { + // Mark as removed from the queue. + m_index_setter(m_heap.front(), std::numeric_limits::max()); + } if (m_heap.size() > 1) { m_heap.front() = m_heap.back(); m_heap.pop_back(); @@ -95,14 +103,18 @@ inline void MutablePriorityQueue::pop() m_heap.clear(); } -template -inline void MutablePriorityQueue::remove(size_t idx) +template +inline void MutablePriorityQueue::remove(size_t idx) { assert(idx < m_heap.size()); -#ifndef NDEBUG - // Mark as removed from the queue. - m_index_setter(m_heap[idx], std::numeric_limits::max()); +#ifdef NDEBUG + // Only mark as removed from the queue in release mode, if configured so. + if (ResetIndexWhenRemoved) #endif /* NDEBUG */ + { + // Mark as removed from the queue. + m_index_setter(m_heap[idx], std::numeric_limits::max()); + } if (idx + 1 == m_heap.size()) { m_heap.pop_back(); return; @@ -114,8 +126,8 @@ inline void MutablePriorityQueue::remove(size_t i update_heap_up(0, idx); } -template -inline void MutablePriorityQueue::update_heap_up(size_t top, size_t bottom) +template +inline void MutablePriorityQueue::update_heap_up(size_t top, size_t bottom) { size_t childIdx = bottom; T *child = &m_heap[childIdx]; @@ -138,8 +150,8 @@ inline void MutablePriorityQueue::update_heap_up( } } -template -inline void MutablePriorityQueue::update_heap_down(size_t top, size_t bottom) +template +inline void MutablePriorityQueue::update_heap_down(size_t top, size_t bottom) { size_t parentIdx = top; T *parent = &m_heap[parentIdx]; diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp index 0ebd6c173..ae6023e7a 100644 --- a/src/libslic3r/ShortestPath.cpp +++ b/src/libslic3r/ShortestPath.cpp @@ -191,7 +191,7 @@ std::vector> chain_segments_greedy_constrained_reversals } // Initialize a heap of end points sorted by the lowest distance to the next valid point of a path. - auto queue = make_mutable_priority_queue( + auto queue = make_mutable_priority_queue( [](EndPoint *ep, size_t idx){ ep->heap_idx = idx; }, [](EndPoint *l, EndPoint *r){ return l->distance_out < r->distance_out; }); queue.reserve(end_points.size() * 2 - 1); @@ -687,7 +687,7 @@ std::vector> chain_segments_greedy_constrained_reversals } // Initialize a heap of end points sorted by the lowest distance to the next valid point of a path. - auto queue = make_mutable_priority_queue( + auto queue = make_mutable_priority_queue( [](EndPoint *ep, size_t idx){ ep->heap_idx = idx; }, [](EndPoint *l, EndPoint *r){ return l->distance_out < r->distance_out; }); queue.reserve(end_points.size() * 2); @@ -1150,7 +1150,7 @@ static inline void improve_ordering_by_segment_flipping(Polylines &polylines, bo #endif /* NDEBUG */ // Initialize a MutablePriorityHeap of connections between polylines. - auto queue = make_mutable_priority_queue( + auto queue = make_mutable_priority_queue( [](Connection *connection, size_t idx){ connection->heap_idx = idx; }, // Sort by decreasing connection distance. [&polylines, &connections](Connection *l, Connection *r){ return l->squaredNorm(polylines, connections) > r->squaredNorm(polylines, connections); }); From b1047d2e265405eeee59382da2ad2edf7c329f54 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 25 Nov 2019 10:30:31 +0100 Subject: [PATCH 72/73] ENABLE_THUMBNAIL_GENERATOR -> ENABLE_THUMBNAIL_GENERATOR_PNG_TO_GCODE set as default --- src/libslic3r/GCode.cpp | 36 ------------------------ src/libslic3r/Technologies.hpp | 1 - src/slic3r/GUI/GUI_App.cpp | 51 ---------------------------------- 3 files changed, 88 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 28bcdf50c..014deb79c 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -32,9 +32,7 @@ #include -#if ENABLE_THUMBNAIL_GENERATOR_PNG_TO_GCODE #include "miniz_extension.hpp" -#endif // ENABLE_THUMBNAIL_GENERATOR_PNG_TO_GCODE #if 0 // Enable debugging and asserts, even in the release build. @@ -998,7 +996,6 @@ void GCode::_do_export(Print& print, FILE* file) { if (data.is_valid()) { -#if ENABLE_THUMBNAIL_GENERATOR_PNG_TO_GCODE size_t png_size = 0; void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)data.pixels.data(), data.width, data.height, 4, &png_size, MZ_DEFAULT_LEVEL, 1); if (png_data != nullptr) @@ -1024,39 +1021,6 @@ void GCode::_do_export(Print& print, FILE* file) mz_free(png_data); } -#else - _write_format(file, "\n;\n; thumbnail begin %dx%d\n", data.width, data.height); - - size_t row_size = 4 * data.width; - for (int r = (int)data.height - 1; r >= 0; --r) - { - std::string encoded; - encoded.resize(boost::beast::detail::base64::encoded_size(row_size)); - encoded.resize(boost::beast::detail::base64::encode((void*)&encoded[0], (const void*)(data.pixels.data() + r * row_size), row_size)); - - unsigned int row_count = 0; - while (encoded.size() > max_row_length) - { - if (row_count == 0) - _write_format(file, "; %s\n", encoded.substr(0, max_row_length).c_str()); - else - _write_format(file, ";>%s\n", encoded.substr(0, max_row_length).c_str()); - - encoded = encoded.substr(max_row_length); - ++row_count; - } - - if (encoded.size() > 0) - { - if (row_count == 0) - _write_format(file, "; %s\n", encoded.c_str()); - else - _write_format(file, ";>%s\n", encoded.c_str()); - } - } - - _write(file, "; thumbnail end\n;\n"); -#endif // ENABLE_THUMBNAIL_GENERATOR_PNG_TO_GCODE } print.throw_if_canceled(); } diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index f8db1b7b3..7a6a682b1 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -40,7 +40,6 @@ // Enable thumbnail generator #define ENABLE_THUMBNAIL_GENERATOR (1 && ENABLE_2_2_0_ALPHA1) #define ENABLE_THUMBNAIL_GENERATOR_DEBUG (0 && ENABLE_THUMBNAIL_GENERATOR) -#define ENABLE_THUMBNAIL_GENERATOR_PNG_TO_GCODE (1 && ENABLE_THUMBNAIL_GENERATOR) // Enable adaptive layer height profile #define ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE (1 && ENABLE_2_2_0_ALPHA1) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 7d37baa66..a74c6b0c0 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1126,7 +1126,6 @@ void GUI_App::gcode_thumbnails_debug() } else if (reading_image && boost::starts_with(gcode_line, END_MASK)) { -#if ENABLE_THUMBNAIL_GENERATOR_PNG_TO_GCODE std::string out_filename = out_path + std::to_string(width) + "x" + std::to_string(height) + ".png"; boost::nowide::ofstream out_file(out_filename.c_str(), std::ios::binary); if (out_file.good()) @@ -1138,46 +1137,6 @@ void GUI_App::gcode_thumbnails_debug() out_file.write(decoded.c_str(), decoded.size()); out_file.close(); } -#else - if (!row.empty()) - { - rows.push_back(row); - row.clear(); - } - - if ((unsigned int)rows.size() == height) - { - std::vector thumbnail(4 * width * height, 0); - for (unsigned int r = 0; r < (unsigned int)rows.size(); ++r) - { - std::string decoded_row; - decoded_row.resize(boost::beast::detail::base64::decoded_size(rows[r].size())); - decoded_row.resize(boost::beast::detail::base64::decode((void*)&decoded_row[0], rows[r].data(), rows[r].size()).first); - - if ((unsigned int)decoded_row.size() == width * 4) - { - void* image_ptr = (void*)(thumbnail.data() + r * width * 4); - ::memcpy(image_ptr, (const void*)decoded_row.c_str(), width * 4); - } - } - - wxImage image(width, height); - image.InitAlpha(); - - for (unsigned int r = 0; r < height; ++r) - { - unsigned int rr = r * width; - for (unsigned int c = 0; c < width; ++c) - { - unsigned char* px = thumbnail.data() + 4 * (rr + c); - image.SetRGB((int)c, (int)r, px[0], px[1], px[2]); - image.SetAlpha((int)c, (int)r, px[3]); - } - } - - image.SaveFile(out_path + std::to_string(width) + "x" + std::to_string(height) + ".png", wxBITMAP_TYPE_PNG); - } -#endif // ENABLE_THUMBNAIL_GENERATOR_PNG_TO_GCODE reading_image = false; width = 0; @@ -1185,17 +1144,7 @@ void GUI_App::gcode_thumbnails_debug() rows.clear(); } else if (reading_image) - { -#if !ENABLE_THUMBNAIL_GENERATOR_PNG_TO_GCODE - if (!row.empty() && (gcode_line[1] == ' ')) - { - rows.push_back(row); - row.clear(); - } -#endif // !ENABLE_THUMBNAIL_GENERATOR_PNG_TO_GCODE - row += gcode_line.substr(2); - } } } From ab00f501f1363802282986ea9ab86d2e305bc24d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 25 Nov 2019 10:34:42 +0100 Subject: [PATCH 73/73] #3230 - Detection of 3Dconnexion devices every 2 seconds --- src/slic3r/GUI/Mouse3DController.cpp | 10 ++++++++++ src/slic3r/GUI/Mouse3DController.hpp | 2 ++ 2 files changed, 12 insertions(+) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 44bc0fd34..39542e652 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -186,6 +186,7 @@ Mouse3DController::Mouse3DController() , m_running(false) , m_settings_dialog(false) { + m_last_time = std::chrono::high_resolution_clock::now(); } void Mouse3DController::init() @@ -332,9 +333,18 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign bool Mouse3DController::connect_device() { + static const long long DETECTION_TIME = 2; // seconds + if (is_device_connected()) return false; + // check time since last detection took place + auto now = std::chrono::high_resolution_clock::now(); + if (std::chrono::duration_cast(now - m_last_time).count() < DETECTION_TIME) + return false; + + m_last_time = now; + // Enumerates devices hid_device_info* devices = hid_enumerate(0, 0); if (devices == nullptr) diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 161274b5a..40c489853 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace Slic3r { namespace GUI { @@ -130,6 +131,7 @@ class Mouse3DController std::string m_device_str; bool m_running; bool m_settings_dialog; + std::chrono::time_point m_last_time; public: Mouse3DController();