mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-05-12 19:38:09 +08:00
Merge remote-tracking branch 'origin' into ys_manipulation_panel_rw
This commit is contained in:
commit
d90a27d5f8
@ -1,3 +1,12 @@
|
|||||||
|
2018-01-17 Joerg Wunsch <j.gnu@uriah.heep.sax.de>
|
||||||
|
(cherry-picked)
|
||||||
|
Submitted by Reinhard Max
|
||||||
|
patch #8311: Add IPv6 support to the -Pnet:host:port option
|
||||||
|
* ser_posix.c (net_open): Rewrite to use getaddrinfo()
|
||||||
|
rather than gethostbyname()
|
||||||
|
* avrdude.1: Document IPv6 feature
|
||||||
|
* doc/avrdude.texi: (Dito)
|
||||||
|
|
||||||
2016-05-10 Joerg Wunsch <j.gnu@uriah.heep.sax.de>
|
2016-05-10 Joerg Wunsch <j.gnu@uriah.heep.sax.de>
|
||||||
|
|
||||||
Submitted by Hannes Jochriem:
|
Submitted by Hannes Jochriem:
|
||||||
|
@ -505,12 +505,19 @@ network connection to (TCP)
|
|||||||
on
|
on
|
||||||
.Ar host
|
.Ar host
|
||||||
is established.
|
is established.
|
||||||
|
Square brackets may be placed around
|
||||||
|
.Ar host
|
||||||
|
to improve readability, for numeric IPv6 addresses (e.g.
|
||||||
|
.Li net:[2001:db8::42]:1337 ) .
|
||||||
The remote endpoint is assumed to be a terminal or console server
|
The remote endpoint is assumed to be a terminal or console server
|
||||||
that connects the network stream to a local serial port where the
|
that connects the network stream to a local serial port where the
|
||||||
actual programmer has been attached to.
|
actual programmer has been attached to.
|
||||||
The port is assumed to be properly configured, for example using a
|
The port is assumed to be properly configured, for example using a
|
||||||
transparent 8-bit data connection without parity at 115200 Baud
|
transparent 8-bit data connection without parity at 115200 Baud
|
||||||
for a STK500.
|
for a STK500.
|
||||||
|
.Pp
|
||||||
|
Note: The ability to handle IPv6 hostnames and addresses is limited to
|
||||||
|
Posix systems (by now).
|
||||||
.It Fl q
|
.It Fl q
|
||||||
Disable (or quell) output of the progress bar while reading or writing
|
Disable (or quell) output of the progress bar while reading or writing
|
||||||
to the device. Specify it a second time for even quieter operation.
|
to the device. Specify it a second time for even quieter operation.
|
||||||
|
@ -214,7 +214,7 @@ AC_HEADER_TIME
|
|||||||
AC_CHECK_LIB([ws2_32], [puts])
|
AC_CHECK_LIB([ws2_32], [puts])
|
||||||
|
|
||||||
# Checks for library functions.
|
# Checks for library functions.
|
||||||
AC_CHECK_FUNCS([memset select strcasecmp strdup strerror strncasecmp strtol strtoul gettimeofday usleep])
|
AC_CHECK_FUNCS([memset select strcasecmp strdup strerror strncasecmp strtol strtoul gettimeofday usleep getaddrinfo])
|
||||||
|
|
||||||
AC_MSG_CHECKING([for a Win32 HID libray])
|
AC_MSG_CHECKING([for a Win32 HID libray])
|
||||||
SAVED_LIBS="${LIBS}"
|
SAVED_LIBS="${LIBS}"
|
||||||
|
@ -557,6 +557,9 @@ higher level protocol (as opposed to bit-bang style programmers),
|
|||||||
In this case, instead of trying to open a local device, a TCP
|
In this case, instead of trying to open a local device, a TCP
|
||||||
network connection to (TCP) @var{port} on @var{host}
|
network connection to (TCP) @var{port} on @var{host}
|
||||||
is established.
|
is established.
|
||||||
|
Square brackets may be placed around @var{host} to improve
|
||||||
|
readability for numeric IPv6 addresses (e.g.
|
||||||
|
@code{net:[2001:db8::42]:1337}).
|
||||||
The remote endpoint is assumed to be a terminal or console server
|
The remote endpoint is assumed to be a terminal or console server
|
||||||
that connects the network stream to a local serial port where the
|
that connects the network stream to a local serial port where the
|
||||||
actual programmer has been attached to.
|
actual programmer has been attached to.
|
||||||
@ -564,6 +567,8 @@ The port is assumed to be properly configured, for example using a
|
|||||||
transparent 8-bit data connection without parity at 115200 Baud
|
transparent 8-bit data connection without parity at 115200 Baud
|
||||||
for a STK500.
|
for a STK500.
|
||||||
|
|
||||||
|
Note: The ability to handle IPv6 hostnames and addresses is limited to
|
||||||
|
Posix systems (by now).
|
||||||
|
|
||||||
@item -q
|
@item -q
|
||||||
Disable (or quell) output of the progress bar while reading or writing
|
Disable (or quell) output of the progress bar while reading or writing
|
||||||
|
@ -150,6 +150,7 @@ static int ser_setspeed(union filedescriptor *fd, long baud)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "ac_cfg.h"
|
||||||
|
|
||||||
// Timeout read & write variants
|
// Timeout read & write variants
|
||||||
// Additionally to the regular -1 on I/O error, they return -2 on timeout
|
// Additionally to the regular -1 on I/O error, they return -2 on timeout
|
||||||
@ -221,23 +222,35 @@ ssize_t write_timeout(int fd, const void *buf, size_t count, long timeout)
|
|||||||
static int
|
static int
|
||||||
net_open(const char *port, union filedescriptor *fdp)
|
net_open(const char *port, union filedescriptor *fdp)
|
||||||
{
|
{
|
||||||
char *hstr, *pstr, *end;
|
#ifdef HAVE_GETADDRINFO
|
||||||
unsigned int pnum;
|
char *hp, *hstr, *pstr;
|
||||||
int fd;
|
int s, fd, ret = -1;
|
||||||
struct sockaddr_in sockaddr;
|
struct addrinfo hints;
|
||||||
struct hostent *hp;
|
struct addrinfo *result, *rp;
|
||||||
|
|
||||||
if ((hstr = strdup(port)) == NULL) {
|
if ((hstr = hp = strdup(port)) == NULL) {
|
||||||
avrdude_message(MSG_INFO, "%s: net_open(): Out of memory!\n",
|
avrdude_message(MSG_INFO, "%s: net_open(): Out of memory!\n",
|
||||||
progname);
|
progname);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (((pstr = strchr(hstr, ':')) == NULL) || (pstr == hstr)) {
|
/*
|
||||||
|
* As numeric IPv6 addresses use colons as separators, we need to
|
||||||
|
* look for the last colon here, which separates the port number or
|
||||||
|
* service name from the host or IP address.
|
||||||
|
*/
|
||||||
|
if (((pstr = strrchr(hstr, ':')) == NULL) || (pstr == hstr)) {
|
||||||
avrdude_message(MSG_INFO, "%s: net_open(): Mangled host:port string \"%s\"\n",
|
avrdude_message(MSG_INFO, "%s: net_open(): Mangled host:port string \"%s\"\n",
|
||||||
progname, hstr);
|
progname, hstr);
|
||||||
free(hstr);
|
goto error;
|
||||||
return -1;
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove brackets from the host part, if present.
|
||||||
|
*/
|
||||||
|
if (*hstr == '[' && *(pstr-1) == ']') {
|
||||||
|
hstr++;
|
||||||
|
*(pstr-1) = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -245,43 +258,49 @@ net_open(const char *port, union filedescriptor *fdp)
|
|||||||
*/
|
*/
|
||||||
*pstr++ = '\0';
|
*pstr++ = '\0';
|
||||||
|
|
||||||
pnum = strtoul(pstr, &end, 10);
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
hints.ai_family = AF_UNSPEC;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
s = getaddrinfo(hstr, pstr, &hints, &result);
|
||||||
|
|
||||||
if ((*pstr == '\0') || (*end != '\0') || (pnum == 0) || (pnum > 65535)) {
|
if (s != 0) {
|
||||||
avrdude_message(MSG_INFO, "%s: net_open(): Bad port number \"%s\"\n",
|
avrdude_message(MSG_INFO,
|
||||||
progname, pstr);
|
"%s: net_open(): Cannot resolve "
|
||||||
free(hstr);
|
"host=\"%s\", port=\"%s\": %s\n",
|
||||||
return -1;
|
progname, hstr, pstr, gai_strerror(s));
|
||||||
|
goto error;
|
||||||
}
|
}
|
||||||
|
for (rp = result; rp != NULL; rp = rp->ai_next) {
|
||||||
if ((hp = gethostbyname(hstr)) == NULL) {
|
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||||
avrdude_message(MSG_INFO, "%s: net_open(): unknown host \"%s\"\n",
|
if (fd == -1) {
|
||||||
progname, hstr);
|
/* This one failed, loop over */
|
||||||
free(hstr);
|
continue;
|
||||||
return -1;
|
}
|
||||||
|
if (connect(fd, rp->ai_addr, rp->ai_addrlen) != -1) {
|
||||||
|
/* Success, we are connected */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
}
|
}
|
||||||
|
if (rp == NULL) {
|
||||||
free(hstr);
|
avrdude_message(MSG_INFO, "%s: net_open(): Cannot connect: %s\n",
|
||||||
|
progname, strerror(errno));
|
||||||
if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
|
|
||||||
avrdude_message(MSG_INFO, "%s: net_open(): Cannot open socket: %s\n",
|
|
||||||
progname, strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
memset(&sockaddr, 0, sizeof(struct sockaddr_in));
|
fdp->ifd = fd;
|
||||||
sockaddr.sin_family = AF_INET;
|
ret = 0;
|
||||||
sockaddr.sin_port = htons(pnum);
|
|
||||||
memcpy(&(sockaddr.sin_addr.s_addr), hp->h_addr, sizeof(struct in_addr));
|
|
||||||
|
|
||||||
if (connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
|
|
||||||
avrdude_message(MSG_INFO, "%s: net_open(): Connect failed: %s\n",
|
|
||||||
progname, strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
freeaddrinfo(result);
|
||||||
|
|
||||||
fdp->ifd = fd;
|
error:
|
||||||
return 0;
|
free(hp);
|
||||||
|
return ret;
|
||||||
|
#else
|
||||||
|
avrdude_message(MSG_INFO,
|
||||||
|
"%s: Networking is not supported on your platform.\n"
|
||||||
|
"If you need it, please open a bug report.\n", progname);
|
||||||
|
return -1;
|
||||||
|
#endif /* HAVE_GETADDRINFO */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,17 +81,16 @@ inline void offset(PolygonImpl& sh, TCoord<PointImpl> distance, const PolygonTag
|
|||||||
using ClipperLib::etClosedPolygon;
|
using ClipperLib::etClosedPolygon;
|
||||||
using ClipperLib::Paths;
|
using ClipperLib::Paths;
|
||||||
|
|
||||||
// If the input is not at least a triangle, we can not do this algorithm
|
|
||||||
if(sh.Contour.size() <= 3 ||
|
|
||||||
std::any_of(sh.Holes.begin(), sh.Holes.end(),
|
|
||||||
[](const PathImpl& p) { return p.size() <= 3; })
|
|
||||||
) throw GeometryException(GeomErr::OFFSET);
|
|
||||||
|
|
||||||
ClipperOffset offs;
|
|
||||||
Paths result;
|
Paths result;
|
||||||
offs.AddPath(sh.Contour, jtMiter, etClosedPolygon);
|
|
||||||
offs.AddPaths(sh.Holes, jtMiter, etClosedPolygon);
|
try {
|
||||||
offs.Execute(result, static_cast<double>(distance));
|
ClipperOffset offs;
|
||||||
|
offs.AddPath(sh.Contour, jtMiter, etClosedPolygon);
|
||||||
|
offs.AddPaths(sh.Holes, jtMiter, etClosedPolygon);
|
||||||
|
offs.Execute(result, static_cast<double>(distance));
|
||||||
|
} catch (ClipperLib::clipperException &) {
|
||||||
|
throw GeometryException(GeomErr::OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
// Offsetting reverts the orientation and also removes the last vertex
|
// Offsetting reverts the orientation and also removes the last vertex
|
||||||
// so boost will not have a closed polygon.
|
// so boost will not have a closed polygon.
|
||||||
|
@ -1144,7 +1144,7 @@ inline bool isInside(const TBGuest& ibb, const TBHost& box,
|
|||||||
auto minY = getY(box.minCorner());
|
auto minY = getY(box.minCorner());
|
||||||
auto maxY = getY(box.maxCorner());
|
auto maxY = getY(box.maxCorner());
|
||||||
|
|
||||||
return iminX > minX && imaxX < maxX && iminY > minY && imaxY < maxY;
|
return iminX >= minX && imaxX <= maxX && iminY >= minY && imaxY <= maxY;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class S, class TB>
|
template<class S, class TB>
|
||||||
|
@ -3,9 +3,6 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
// For caching nfps
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
// For parallel for
|
// For parallel for
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
@ -76,55 +73,6 @@ inline void enumerate(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace __itemhash {
|
|
||||||
|
|
||||||
using Key = size_t;
|
|
||||||
|
|
||||||
template<class S>
|
|
||||||
Key hash(const _Item<S>& item) {
|
|
||||||
using Point = TPoint<S>;
|
|
||||||
using Segment = _Segment<Point>;
|
|
||||||
|
|
||||||
static const int N = 26;
|
|
||||||
static const int M = N*N - 1;
|
|
||||||
|
|
||||||
std::string ret;
|
|
||||||
auto& rhs = item.rawShape();
|
|
||||||
auto& ctr = sl::contour(rhs);
|
|
||||||
auto it = ctr.begin();
|
|
||||||
auto nx = std::next(it);
|
|
||||||
|
|
||||||
double circ = 0;
|
|
||||||
while(nx != ctr.end()) {
|
|
||||||
Segment seg(*it++, *nx++);
|
|
||||||
Radians a = seg.angleToXaxis();
|
|
||||||
double deg = Degrees(a);
|
|
||||||
int ms = 'A', ls = 'A';
|
|
||||||
while(deg > N) { ms++; deg -= N; }
|
|
||||||
ls += int(deg);
|
|
||||||
ret.push_back(char(ms)); ret.push_back(char(ls));
|
|
||||||
circ += std::sqrt(seg.template sqlength<double>());
|
|
||||||
}
|
|
||||||
|
|
||||||
it = ctr.begin(); nx = std::next(it);
|
|
||||||
|
|
||||||
while(nx != ctr.end()) {
|
|
||||||
Segment seg(*it++, *nx++);
|
|
||||||
auto l = int(M * std::sqrt(seg.template sqlength<double>()) / circ);
|
|
||||||
int ms = 'A', ls = 'A';
|
|
||||||
while(l > N) { ms++; l -= N; }
|
|
||||||
ls += l;
|
|
||||||
ret.push_back(char(ms)); ret.push_back(char(ls));
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::hash<std::string>()(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class S>
|
|
||||||
using Hash = std::unordered_map<Key, nfp::NfpResult<S>>;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace placers {
|
namespace placers {
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
@ -529,17 +477,9 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin
|
|||||||
|
|
||||||
using MaxNfpLevel = nfp::MaxNfpLevel<RawShape>;
|
using MaxNfpLevel = nfp::MaxNfpLevel<RawShape>;
|
||||||
|
|
||||||
using ItemKeys = std::vector<__itemhash::Key>;
|
|
||||||
|
|
||||||
// Norming factor for the optimization function
|
// Norming factor for the optimization function
|
||||||
const double norm_;
|
const double norm_;
|
||||||
|
|
||||||
// Caching calculated nfps
|
|
||||||
__itemhash::Hash<RawShape> nfpcache_;
|
|
||||||
|
|
||||||
// Storing item hash keys
|
|
||||||
ItemKeys item_keys_;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
using Pile = nfp::Shapes<RawShape>;
|
using Pile = nfp::Shapes<RawShape>;
|
||||||
@ -636,15 +576,12 @@ public:
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
using Shapes = TMultiShape<RawShape>;
|
using Shapes = TMultiShape<RawShape>;
|
||||||
using ItemRef = std::reference_wrapper<Item>;
|
|
||||||
using ItemWithHash = const std::pair<ItemRef, __itemhash::Key>;
|
|
||||||
|
|
||||||
Shapes calcnfp(const ItemWithHash itsh, Lvl<nfp::NfpLevel::CONVEX_ONLY>)
|
Shapes calcnfp(const Item &trsh, Lvl<nfp::NfpLevel::CONVEX_ONLY>)
|
||||||
{
|
{
|
||||||
using namespace nfp;
|
using namespace nfp;
|
||||||
|
|
||||||
Shapes nfps(items_.size());
|
Shapes nfps(items_.size());
|
||||||
const Item& trsh = itsh.first;
|
|
||||||
|
|
||||||
// /////////////////////////////////////////////////////////////////////
|
// /////////////////////////////////////////////////////////////////////
|
||||||
// TODO: this is a workaround and should be solved in Item with mutexes
|
// TODO: this is a workaround and should be solved in Item with mutexes
|
||||||
@ -678,12 +615,11 @@ private:
|
|||||||
|
|
||||||
|
|
||||||
template<class Level>
|
template<class Level>
|
||||||
Shapes calcnfp( const ItemWithHash itsh, Level)
|
Shapes calcnfp(const Item &trsh, Level)
|
||||||
{ // Function for arbitrary level of nfp implementation
|
{ // Function for arbitrary level of nfp implementation
|
||||||
using namespace nfp;
|
using namespace nfp;
|
||||||
|
|
||||||
Shapes nfps;
|
Shapes nfps;
|
||||||
const Item& trsh = itsh.first;
|
|
||||||
|
|
||||||
auto& orb = trsh.transformedShape();
|
auto& orb = trsh.transformedShape();
|
||||||
bool orbconvex = trsh.isContourConvex();
|
bool orbconvex = trsh.isContourConvex();
|
||||||
@ -849,8 +785,6 @@ private:
|
|||||||
remlist.insert(remlist.end(), remaining.from, remaining.to);
|
remlist.insert(remlist.end(), remaining.from, remaining.to);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t itemhash = __itemhash::hash(item);
|
|
||||||
|
|
||||||
if(items_.empty()) {
|
if(items_.empty()) {
|
||||||
setInitialPosition(item);
|
setInitialPosition(item);
|
||||||
best_overfit = overfit(item.transformedShape(), bin_);
|
best_overfit = overfit(item.transformedShape(), bin_);
|
||||||
@ -875,7 +809,7 @@ private:
|
|||||||
// it is disjunct from the current merged pile
|
// it is disjunct from the current merged pile
|
||||||
placeOutsideOfBin(item);
|
placeOutsideOfBin(item);
|
||||||
|
|
||||||
nfps = calcnfp({item, itemhash}, Lvl<MaxNfpLevel::value>());
|
nfps = calcnfp(item, Lvl<MaxNfpLevel::value>());
|
||||||
|
|
||||||
auto iv = item.referenceVertex();
|
auto iv = item.referenceVertex();
|
||||||
|
|
||||||
@ -1112,7 +1046,6 @@ private:
|
|||||||
|
|
||||||
if(can_pack) {
|
if(can_pack) {
|
||||||
ret = PackResult(item);
|
ret = PackResult(item);
|
||||||
item_keys_.emplace_back(itemhash);
|
|
||||||
} else {
|
} else {
|
||||||
ret = PackResult(best_overfit);
|
ret = PackResult(best_overfit);
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ protected:
|
|||||||
|
|
||||||
Placer p{bin};
|
Placer p{bin};
|
||||||
p.configure(pcfg);
|
p.configure(pcfg);
|
||||||
if (!p.pack(cpy)) it = c.erase(it);
|
if (itm.area() <= 0 || !p.pack(cpy)) it = c.erase(it);
|
||||||
else it++;
|
else it++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ struct NfpImpl<S, NfpLevel::CONVEX_ONLY>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<libnest2d::Item>& prusaParts() {
|
static std::vector<libnest2d::Item>& prusaParts() {
|
||||||
static std::vector<libnest2d::Item> ret;
|
static std::vector<libnest2d::Item> ret;
|
||||||
|
|
||||||
if(ret.empty()) {
|
if(ret.empty()) {
|
||||||
@ -51,7 +51,7 @@ std::vector<libnest2d::Item>& prusaParts() {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BasicFunctionality, Angles)
|
TEST(GeometryAlgorithms, Angles)
|
||||||
{
|
{
|
||||||
|
|
||||||
using namespace libnest2d;
|
using namespace libnest2d;
|
||||||
@ -109,7 +109,7 @@ TEST(BasicFunctionality, Angles)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Simple test, does not use gmock
|
// Simple test, does not use gmock
|
||||||
TEST(BasicFunctionality, creationAndDestruction)
|
TEST(Nesting, ItemCreationAndDestruction)
|
||||||
{
|
{
|
||||||
using namespace libnest2d;
|
using namespace libnest2d;
|
||||||
|
|
||||||
@ -572,26 +572,74 @@ TEST(GeometryAlgorithms, convexHull) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST(GeometryAlgorithms, NestTest) {
|
TEST(Nesting, NestPrusaPartsShouldFitIntoTwoBins) {
|
||||||
|
|
||||||
|
// Get the input items and define the bin.
|
||||||
std::vector<Item> input = prusaParts();
|
std::vector<Item> input = prusaParts();
|
||||||
|
auto bin = Box(250000000, 210000000);
|
||||||
libnest2d::nest(input, Box(250000000, 210000000), [](unsigned cnt) {
|
|
||||||
std::cout << "parts left: " << cnt << std::endl;
|
// Do the nesting. Check in each step if the remaining items are less than
|
||||||
|
// in the previous step. (Some algorithms can place more items in one step)
|
||||||
|
size_t pcount = input.size();
|
||||||
|
libnest2d::nest(input, bin, [&pcount](unsigned cnt) {
|
||||||
|
ASSERT_TRUE(cnt < pcount);
|
||||||
|
pcount = cnt;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Get the number of logical bins: search for the max binId...
|
||||||
auto max_binid_it = std::max_element(input.begin(), input.end(),
|
auto max_binid_it = std::max_element(input.begin(), input.end(),
|
||||||
[](const Item &i1, const Item &i2) {
|
[](const Item &i1, const Item &i2) {
|
||||||
return i1.binId() < i2.binId();
|
return i1.binId() < i2.binId();
|
||||||
});
|
});
|
||||||
|
|
||||||
size_t bins = max_binid_it == input.end() ? 0 : max_binid_it->binId() + 1;
|
|
||||||
|
|
||||||
ASSERT_EQ(bins, 2u);
|
auto bins = size_t(max_binid_it == input.end() ? 0 :
|
||||||
|
max_binid_it->binId() + 1);
|
||||||
|
|
||||||
|
// For prusa parts, 2 bins should be enough...
|
||||||
|
ASSERT_LE(bins, 2u);
|
||||||
|
|
||||||
|
// All parts should be processed by the algorithm
|
||||||
ASSERT_TRUE(
|
ASSERT_TRUE(
|
||||||
std::all_of(input.begin(), input.end(), [](const Item &itm) {
|
std::all_of(input.begin(), input.end(), [](const Item &itm) {
|
||||||
return itm.binId() != BIN_ID_UNSET;
|
return itm.binId() != BIN_ID_UNSET;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Gather the items into piles of arranged polygons...
|
||||||
|
using Pile = TMultiShape<ClipperLib::Polygon>;
|
||||||
|
std::vector<Pile> piles(bins);
|
||||||
|
|
||||||
|
for (auto &itm : input)
|
||||||
|
piles[size_t(itm.binId())].emplace_back(itm.transformedShape());
|
||||||
|
|
||||||
|
// Now check all the piles, the bounding box of each pile should be inside
|
||||||
|
// the defined bin.
|
||||||
|
for (auto &pile : piles) {
|
||||||
|
auto bb = sl::boundingBox(pile);
|
||||||
|
ASSERT_TRUE(sl::isInside(bb, bin));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Nesting, NestEmptyItemShouldBeUntouched) {
|
||||||
|
auto bin = Box(250000000, 210000000); // dummy bin
|
||||||
|
|
||||||
|
std::vector<Item> items;
|
||||||
|
items.emplace_back(Item{}); // Emplace empty item
|
||||||
|
items.emplace_back(Item{0, 200, 0}); // Emplace zero area item
|
||||||
|
|
||||||
|
libnest2d::nest(items, bin);
|
||||||
|
|
||||||
|
for (auto &itm : items) ASSERT_EQ(itm.binId(), BIN_ID_UNSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Nesting, NestLargeItemShouldBeUntouched) {
|
||||||
|
auto bin = Box(250000000, 210000000); // dummy bin
|
||||||
|
|
||||||
|
std::vector<Item> items;
|
||||||
|
items.emplace_back(Rectangle{250000001, 210000001}); // Emplace large item
|
||||||
|
|
||||||
|
libnest2d::nest(items, bin);
|
||||||
|
|
||||||
|
ASSERT_EQ(items.front().binId(), BIN_ID_UNSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -966,26 +1014,20 @@ using Ratio = boost::rational<boost::multiprecision::int128_t>;
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RotatingCalipers, MinAreaBBCClk) {
|
//TEST(GeometryAlgorithms, MinAreaBBCClk) {
|
||||||
auto u = [](ClipperLib::cInt n) { return n*1000000; };
|
// auto u = [](ClipperLib::cInt n) { return n*1000000; };
|
||||||
PolygonImpl poly({ {u(0), u(0)}, {u(4), u(1)}, {u(2), u(4)}});
|
// PolygonImpl poly({ {u(0), u(0)}, {u(4), u(1)}, {u(2), u(4)}});
|
||||||
|
|
||||||
long double arearef = refMinAreaBox(poly);
|
// long double arearef = refMinAreaBox(poly);
|
||||||
long double area = minAreaBoundingBox<PolygonImpl, Unit, Ratio>(poly).area();
|
// long double area = minAreaBoundingBox<PolygonImpl, Unit, Ratio>(poly).area();
|
||||||
|
|
||||||
ASSERT_LE(std::abs(area - arearef), 500e6 );
|
// ASSERT_LE(std::abs(area - arearef), 500e6 );
|
||||||
}
|
//}
|
||||||
|
|
||||||
TEST(RotatingCalipers, AllPrusaMinBB) {
|
TEST(GeometryAlgorithms, MinAreaBBWithRotatingCalipers) {
|
||||||
// /size_t idx = 0;
|
|
||||||
long double err_epsilon = 500e6l;
|
long double err_epsilon = 500e6l;
|
||||||
|
|
||||||
for(ClipperLib::Path rinput : PRINTER_PART_POLYGONS) {
|
for(ClipperLib::Path rinput : PRINTER_PART_POLYGONS) {
|
||||||
// ClipperLib::Path rinput = PRINTER_PART_POLYGONS[idx];
|
|
||||||
// rinput.pop_back();
|
|
||||||
// std::reverse(rinput.begin(), rinput.end());
|
|
||||||
|
|
||||||
// PolygonImpl poly(removeCollinearPoints<PathImpl, PointImpl, Unit>(rinput, 1000000));
|
|
||||||
PolygonImpl poly(rinput);
|
PolygonImpl poly(rinput);
|
||||||
|
|
||||||
long double arearef = refMinAreaBox(poly);
|
long double arearef = refMinAreaBox(poly);
|
||||||
@ -993,8 +1035,6 @@ TEST(RotatingCalipers, AllPrusaMinBB) {
|
|||||||
long double area = cast<long double>(bb.area());
|
long double area = cast<long double>(bb.area());
|
||||||
|
|
||||||
bool succ = std::abs(arearef - area) < err_epsilon;
|
bool succ = std::abs(arearef - area) < err_epsilon;
|
||||||
// std::cout << idx++ << " " << (succ? "ok" : "failed") << " ref: "
|
|
||||||
// << arearef << " actual: " << area << std::endl;
|
|
||||||
|
|
||||||
ASSERT_TRUE(succ);
|
ASSERT_TRUE(succ);
|
||||||
}
|
}
|
||||||
@ -1011,8 +1051,6 @@ TEST(RotatingCalipers, AllPrusaMinBB) {
|
|||||||
|
|
||||||
|
|
||||||
bool succ = std::abs(arearef - area) < err_epsilon;
|
bool succ = std::abs(arearef - area) < err_epsilon;
|
||||||
// std::cout << idx++ << " " << (succ? "ok" : "failed") << " ref: "
|
|
||||||
// << arearef << " actual: " << area << std::endl;
|
|
||||||
|
|
||||||
ASSERT_TRUE(succ);
|
ASSERT_TRUE(succ);
|
||||||
}
|
}
|
||||||
|
@ -618,19 +618,21 @@ void arrange(ArrangePolygons & arrangables,
|
|||||||
items.reserve(arrangables.size());
|
items.reserve(arrangables.size());
|
||||||
|
|
||||||
// Create Item from Arrangeable
|
// Create Item from Arrangeable
|
||||||
auto process_arrangeable =
|
auto process_arrangeable = [](const ArrangePolygon &arrpoly,
|
||||||
[](const ArrangePolygon &arrpoly, std::vector<Item> &outp)
|
std::vector<Item> & outp)
|
||||||
{
|
{
|
||||||
Polygon p = arrpoly.poly.contour;
|
Polygon p = arrpoly.poly.contour;
|
||||||
const Vec2crd & offs = arrpoly.translation;
|
const Vec2crd &offs = arrpoly.translation;
|
||||||
double rotation = arrpoly.rotation;
|
double rotation = arrpoly.rotation;
|
||||||
|
|
||||||
if (p.is_counter_clockwise()) p.reverse();
|
if (p.is_counter_clockwise()) p.reverse();
|
||||||
|
|
||||||
clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p));
|
clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p));
|
||||||
|
|
||||||
auto firstp = clpath.Contour.front();
|
if (!clpath.Contour.empty()) {
|
||||||
clpath.Contour.emplace_back(firstp);
|
auto firstp = clpath.Contour.front();
|
||||||
|
clpath.Contour.emplace_back(firstp);
|
||||||
|
}
|
||||||
|
|
||||||
outp.emplace_back(std::move(clpath));
|
outp.emplace_back(std::move(clpath));
|
||||||
outp.back().rotation(rotation);
|
outp.back().rotation(rotation);
|
||||||
|
@ -359,7 +359,7 @@ std::string GCodePreviewData::get_legend_title() const
|
|||||||
case Extrusion::Feedrate:
|
case Extrusion::Feedrate:
|
||||||
return L("Speed (mm/s)");
|
return L("Speed (mm/s)");
|
||||||
case Extrusion::VolumetricRate:
|
case Extrusion::VolumetricRate:
|
||||||
return L("Volumetric flow rate (mm3/s)");
|
return L("Volumetric flow rate (mm³/s)");
|
||||||
case Extrusion::Tool:
|
case Extrusion::Tool:
|
||||||
return L("Tool");
|
return L("Tool");
|
||||||
case Extrusion::ColorPrint:
|
case Extrusion::ColorPrint:
|
||||||
|
@ -1236,7 +1236,8 @@ std::string Print::validate() const
|
|||||||
|
|
||||||
// The comparison of the profiles is not just about element-wise equality, some layers may not be
|
// The comparison of the profiles is not just about element-wise equality, some layers may not be
|
||||||
// explicitely included. Always remember z and height of last reference layer that in the vector
|
// explicitely included. Always remember z and height of last reference layer that in the vector
|
||||||
// and compare to that.
|
// and compare to that. In case some layers are in the vectors multiple times, only the last entry is
|
||||||
|
// taken into account and compared.
|
||||||
size_t i = 0; // index into tested profile
|
size_t i = 0; // index into tested profile
|
||||||
size_t j = 0; // index into reference profile
|
size_t j = 0; // index into reference profile
|
||||||
coordf_t ref_z = -1.;
|
coordf_t ref_z = -1.;
|
||||||
@ -1244,8 +1245,12 @@ std::string Print::validate() const
|
|||||||
coordf_t ref_height = -1.;
|
coordf_t ref_height = -1.;
|
||||||
while (i < layer_height_profile.size()) {
|
while (i < layer_height_profile.size()) {
|
||||||
coordf_t this_z = layer_height_profile[i];
|
coordf_t this_z = layer_height_profile[i];
|
||||||
|
// find the last entry with this z
|
||||||
|
while (i+2 < layer_height_profile.size() && layer_height_profile[i+2] == this_z)
|
||||||
|
i += 2;
|
||||||
|
|
||||||
coordf_t this_height = layer_height_profile[i+1];
|
coordf_t this_height = layer_height_profile[i+1];
|
||||||
if (next_ref_z < this_z + EPSILON) {
|
if (ref_height < -1. || next_ref_z < this_z + EPSILON) {
|
||||||
ref_z = next_ref_z;
|
ref_z = next_ref_z;
|
||||||
do { // one layer can be in the vector several times
|
do { // one layer can be in the vector several times
|
||||||
ref_height = layer_height_profile_tallest[j+1];
|
ref_height = layer_height_profile_tallest[j+1];
|
||||||
|
@ -42,7 +42,7 @@ static wxString generate_html_row(const Config::Snapshot &snapshot, bool row_eve
|
|||||||
text += " (" + wxString::FromUTF8(snapshot.comment.data()) + ")";
|
text += " (" + wxString::FromUTF8(snapshot.comment.data()) + ")";
|
||||||
text += "</b></font><br>";
|
text += "</b></font><br>";
|
||||||
// End of row header.
|
// End of row header.
|
||||||
text += _(L("slic3r version")) + ": " + snapshot.slic3r_version_captured.to_string() + "<br>";
|
text += _(L("PrusaSlicer version")) + ": " + snapshot.slic3r_version_captured.to_string() + "<br>";
|
||||||
text += _(L("print")) + ": " + snapshot.print + "<br>";
|
text += _(L("print")) + ": " + snapshot.print + "<br>";
|
||||||
text += _(L("filaments")) + ": " + snapshot.filaments.front() + "<br>";
|
text += _(L("filaments")) + ": " + snapshot.filaments.front() + "<br>";
|
||||||
text += _(L("printer")) + ": " + snapshot.printer + "<br>";
|
text += _(L("printer")) + ": " + snapshot.printer + "<br>";
|
||||||
@ -50,9 +50,9 @@ static wxString generate_html_row(const Config::Snapshot &snapshot, bool row_eve
|
|||||||
bool compatible = true;
|
bool compatible = true;
|
||||||
for (const Config::Snapshot::VendorConfig &vc : snapshot.vendor_configs) {
|
for (const Config::Snapshot::VendorConfig &vc : snapshot.vendor_configs) {
|
||||||
text += _(L("vendor")) + ": " + vc.name +", " + _(L("version")) + ": " + vc.version.config_version.to_string() +
|
text += _(L("vendor")) + ": " + vc.name +", " + _(L("version")) + ": " + vc.version.config_version.to_string() +
|
||||||
", " + _(L("min slic3r version")) + ": " + vc.version.min_slic3r_version.to_string();
|
", " + _(L("min PrusaSlicer version")) + ": " + vc.version.min_slic3r_version.to_string();
|
||||||
if (vc.version.max_slic3r_version != Semver::inf())
|
if (vc.version.max_slic3r_version != Semver::inf())
|
||||||
text += ", " + _(L("max slic3r version")) + ": " + vc.version.max_slic3r_version.to_string();
|
text += ", " + _(L("max PrusaSlicer version")) + ": " + vc.version.max_slic3r_version.to_string();
|
||||||
text += "<br>";
|
text += "<br>";
|
||||||
for (const std::pair<std::string, std::set<std::string>> &model : vc.models_variants_installed) {
|
for (const std::pair<std::string, std::set<std::string>> &model : vc.models_variants_installed) {
|
||||||
text += _(L("model")) + ": " + model.first + ", " + _(L("variants")) + ": ";
|
text += _(L("model")) + ": " + model.first + ", " + _(L("variants")) + ": ";
|
||||||
|
@ -1094,7 +1094,7 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const
|
|||||||
wxDEFINE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent);
|
wxDEFINE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent);
|
||||||
wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
|
wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
|
||||||
wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
|
wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
|
||||||
wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent);
|
wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent);
|
||||||
wxDEFINE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent);
|
wxDEFINE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent);
|
||||||
wxDEFINE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent);
|
wxDEFINE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent);
|
||||||
wxDEFINE_EVENT(EVT_GLCANVAS_SELECT_ALL, SimpleEvent);
|
wxDEFINE_EVENT(EVT_GLCANVAS_SELECT_ALL, SimpleEvent);
|
||||||
@ -3012,15 +3012,16 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||||||
wxGetApp().obj_manipul()->set_dirty();
|
wxGetApp().obj_manipul()->set_dirty();
|
||||||
// forces a frame render to update the view before the context menu is shown
|
// forces a frame render to update the view before the context menu is shown
|
||||||
render();
|
render();
|
||||||
|
|
||||||
Vec2d logical_pos = pos.cast<double>();
|
|
||||||
#if ENABLE_RETINA_GL
|
|
||||||
const float factor = m_retina_helper->get_scale_factor();
|
|
||||||
logical_pos = logical_pos.cwiseQuotient(Vec2d(factor, factor));
|
|
||||||
#endif // ENABLE_RETINA_GL
|
|
||||||
post_event(Vec2dEvent(EVT_GLCANVAS_RIGHT_CLICK, logical_pos));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Vec2d logical_pos = pos.cast<double>();
|
||||||
|
#if ENABLE_RETINA_GL
|
||||||
|
const float factor = m_retina_helper->get_scale_factor();
|
||||||
|
logical_pos = logical_pos.cwiseQuotient(Vec2d(factor, factor));
|
||||||
|
#endif // ENABLE_RETINA_GL
|
||||||
|
if (!m_mouse.dragging)
|
||||||
|
// do not post the event if the user is panning the scene
|
||||||
|
post_event(RBtnEvent(EVT_GLCANVAS_RIGHT_CLICK, { logical_pos, m_hover_volume_idxs.empty() }));
|
||||||
}
|
}
|
||||||
|
|
||||||
mouse_up_cleanup();
|
mouse_up_cleanup();
|
||||||
@ -3372,7 +3373,7 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type)
|
|||||||
void GLCanvas3D::set_camera_zoom(double zoom)
|
void GLCanvas3D::set_camera_zoom(double zoom)
|
||||||
{
|
{
|
||||||
const Size& cnv_size = get_canvas_size();
|
const Size& cnv_size = get_canvas_size();
|
||||||
m_camera.set_zoom(zoom, _max_bounding_box(false, false), cnv_size.get_width(), cnv_size.get_height());
|
m_camera.set_zoom(zoom, _max_bounding_box(false, true), cnv_size.get_width(), cnv_size.get_height());
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,6 +71,8 @@ public:
|
|||||||
wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
|
wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
|
||||||
|
|
||||||
using Vec2dEvent = Event<Vec2d>;
|
using Vec2dEvent = Event<Vec2d>;
|
||||||
|
// _bool_ value is used as a indicator of selection in the 3DScene
|
||||||
|
using RBtnEvent = Event<std::pair<Vec2d, bool>>;
|
||||||
template <size_t N> using Vec2dsEvent = ArrayEvent<Vec2d, N>;
|
template <size_t N> using Vec2dsEvent = ArrayEvent<Vec2d, N>;
|
||||||
|
|
||||||
using Vec3dEvent = Event<Vec3d>;
|
using Vec3dEvent = Event<Vec3d>;
|
||||||
@ -78,7 +80,7 @@ template <size_t N> using Vec3dsEvent = ArrayEvent<Vec3d, N>;
|
|||||||
|
|
||||||
wxDECLARE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent);
|
wxDECLARE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent);
|
||||||
wxDECLARE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
|
wxDECLARE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
|
||||||
wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent);
|
wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent);
|
||||||
wxDECLARE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent);
|
wxDECLARE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent);
|
||||||
wxDECLARE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent);
|
wxDECLARE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent);
|
||||||
wxDECLARE_EVENT(EVT_GLCANVAS_SELECT_ALL, SimpleEvent);
|
wxDECLARE_EVENT(EVT_GLCANVAS_SELECT_ALL, SimpleEvent);
|
||||||
|
@ -255,21 +255,32 @@ void ObjectList::create_objects_ctrl()
|
|||||||
EnableDropTarget(wxDF_UNICODETEXT);
|
EnableDropTarget(wxDF_UNICODETEXT);
|
||||||
#endif // wxUSE_DRAG_AND_DROP && wxUSE_UNICODE
|
#endif // wxUSE_DRAG_AND_DROP && wxUSE_UNICODE
|
||||||
|
|
||||||
|
const int em = wxGetApp().em_unit();
|
||||||
|
|
||||||
// column ItemName(Icon+Text) of the view control:
|
// column ItemName(Icon+Text) of the view control:
|
||||||
// And Icon can be consisting of several bitmaps
|
// And Icon can be consisting of several bitmaps
|
||||||
AppendColumn(new wxDataViewColumn(_(L("Name")), new BitmapTextRenderer(),
|
AppendColumn(new wxDataViewColumn(_(L("Name")), new BitmapTextRenderer(),
|
||||||
colName, 20*wxGetApp().em_unit()/*200*/, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE));
|
colName, 20*em, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE));
|
||||||
|
|
||||||
// column PrintableProperty (Icon) of the view control:
|
// column PrintableProperty (Icon) of the view control:
|
||||||
AppendBitmapColumn(" ", colPrint, wxDATAVIEW_CELL_INERT, int(2 * wxGetApp().em_unit()),
|
AppendBitmapColumn(" ", colPrint, wxDATAVIEW_CELL_INERT, 3*em,
|
||||||
wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE);
|
wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE);
|
||||||
|
|
||||||
// column Extruder of the view control:
|
// column Extruder of the view control:
|
||||||
AppendColumn(create_objects_list_extruder_column(4));
|
AppendColumn(create_objects_list_extruder_column(4));
|
||||||
|
|
||||||
// column ItemEditing of the view control:
|
// column ItemEditing of the view control:
|
||||||
AppendBitmapColumn(_(L("Editing")), colEditing, wxDATAVIEW_CELL_INERT, int(2.5 * wxGetApp().em_unit())/*25*/,
|
AppendBitmapColumn(_(L("Editing")), colEditing, wxDATAVIEW_CELL_INERT, 3*em,
|
||||||
wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE);
|
wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE);
|
||||||
|
|
||||||
|
// For some reason under OSX on 4K(5K) monitors in wxDataViewColumn constructor doesn't set width of column.
|
||||||
|
// Therefore, force set column width.
|
||||||
|
if (wxOSX)
|
||||||
|
{
|
||||||
|
GetColumn(colName)->SetWidth(20*em);
|
||||||
|
GetColumn(colPrint)->SetWidth(3*em);
|
||||||
|
GetColumn(colExtruder)->SetWidth(8*em);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::create_popup_menus()
|
void ObjectList::create_popup_menus()
|
||||||
@ -801,8 +812,13 @@ void ObjectList::list_manipulation(bool evt_context_menu/* = false*/)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if (!item) {
|
if (!item) {
|
||||||
if (wxOSX && col == nullptr)
|
if (col == nullptr) {
|
||||||
UnselectAll();
|
if (wxOSX)
|
||||||
|
UnselectAll();
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (evt_context_menu) {
|
if (evt_context_menu) {
|
||||||
show_context_menu(evt_context_menu);
|
show_context_menu(evt_context_menu);
|
||||||
return;
|
return;
|
||||||
@ -822,12 +838,18 @@ void ObjectList::list_manipulation(bool evt_context_menu/* = false*/)
|
|||||||
show_context_menu(evt_context_menu);
|
show_context_menu(evt_context_menu);
|
||||||
else if (title == _("Name"))
|
else if (title == _("Name"))
|
||||||
{
|
{
|
||||||
int obj_idx, vol_idx;
|
if (wxOSX)
|
||||||
get_selected_item_indexes(obj_idx, vol_idx, item);
|
show_context_menu(evt_context_menu); // return context menu under OSX (related to #2909)
|
||||||
|
|
||||||
if (is_windows10() && get_mesh_errors_count(obj_idx, vol_idx) > 0 &&
|
if (is_windows10())
|
||||||
pt.x > 2*wxGetApp().em_unit() && pt.x < 4*wxGetApp().em_unit() )
|
{
|
||||||
fix_through_netfabb();
|
int obj_idx, vol_idx;
|
||||||
|
get_selected_item_indexes(obj_idx, vol_idx, item);
|
||||||
|
|
||||||
|
if (get_mesh_errors_count(obj_idx, vol_idx) > 0 &&
|
||||||
|
pt.x > 2*wxGetApp().em_unit() && pt.x < 4*wxGetApp().em_unit() )
|
||||||
|
fix_through_netfabb();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __WXMSW__
|
#ifndef __WXMSW__
|
||||||
@ -1315,7 +1337,7 @@ wxMenu* ObjectList::append_submenu_add_generic(wxMenu* menu, const ModelVolumeTy
|
|||||||
|
|
||||||
for (auto& item : { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") })
|
for (auto& item : { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") })
|
||||||
{
|
{
|
||||||
if (type == ModelVolumeType::INVALID && item == "Slab")
|
if (type == ModelVolumeType::INVALID && strncmp(item, "Slab", 4) == 0)
|
||||||
continue;
|
continue;
|
||||||
append_menu_item(sub_menu, wxID_ANY, _(item), "",
|
append_menu_item(sub_menu, wxID_ANY, _(item), "",
|
||||||
[this, type, item](wxCommandEvent&) { load_generic_subobject(item, type); }, "", menu);
|
[this, type, item](wxCommandEvent&) { load_generic_subobject(item, type); }, "", menu);
|
||||||
@ -3663,10 +3685,10 @@ void ObjectList::msw_rescale()
|
|||||||
// update min size !!! A width of control shouldn't be a wxDefaultCoord
|
// update min size !!! A width of control shouldn't be a wxDefaultCoord
|
||||||
SetMinSize(wxSize(1, 15 * em));
|
SetMinSize(wxSize(1, 15 * em));
|
||||||
|
|
||||||
GetColumn(colName)->SetWidth(19 * em);
|
GetColumn(colName )->SetWidth(20 * em);
|
||||||
GetColumn(colPrint)->SetWidth( 2 * em);
|
GetColumn(colPrint )->SetWidth( 3 * em);
|
||||||
GetColumn(colExtruder)->SetWidth( 8 * em);
|
GetColumn(colExtruder)->SetWidth( 8 * em);
|
||||||
GetColumn(colEditing)->SetWidth( 2 * em);
|
GetColumn(colEditing )->SetWidth( 3 * em);
|
||||||
|
|
||||||
// rescale all icons, used by ObjectList
|
// rescale all icons, used by ObjectList
|
||||||
msw_rescale_icons();
|
msw_rescale_icons();
|
||||||
|
@ -1340,6 +1340,8 @@ struct Plater::priv
|
|||||||
MenuWithSeparators part_menu;
|
MenuWithSeparators part_menu;
|
||||||
// SLA-Object popup menu
|
// SLA-Object popup menu
|
||||||
MenuWithSeparators sla_object_menu;
|
MenuWithSeparators sla_object_menu;
|
||||||
|
// Default popup menu (when nothing is selected on 3DScene)
|
||||||
|
MenuWithSeparators default_menu;
|
||||||
|
|
||||||
// Removed/Prepended Items according to the view mode
|
// Removed/Prepended Items according to the view mode
|
||||||
std::vector<wxMenuItem*> items_increase;
|
std::vector<wxMenuItem*> items_increase;
|
||||||
@ -1879,7 +1881,7 @@ struct Plater::priv
|
|||||||
void on_action_layersediting(SimpleEvent&);
|
void on_action_layersediting(SimpleEvent&);
|
||||||
|
|
||||||
void on_object_select(SimpleEvent&);
|
void on_object_select(SimpleEvent&);
|
||||||
void on_right_click(Vec2dEvent&);
|
void on_right_click(RBtnEvent&);
|
||||||
void on_wipetower_moved(Vec3dEvent&);
|
void on_wipetower_moved(Vec3dEvent&);
|
||||||
void on_wipetower_rotated(Vec3dEvent&);
|
void on_wipetower_rotated(Vec3dEvent&);
|
||||||
void on_update_geometry(Vec3dsEvent<2>&);
|
void on_update_geometry(Vec3dsEvent<2>&);
|
||||||
@ -3446,57 +3448,66 @@ void Plater::priv::on_object_select(SimpleEvent& evt)
|
|||||||
selection_changed();
|
selection_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::priv::on_right_click(Vec2dEvent& evt)
|
void Plater::priv::on_right_click(RBtnEvent& evt)
|
||||||
{
|
{
|
||||||
int obj_idx = get_selected_object_idx();
|
int obj_idx = get_selected_object_idx();
|
||||||
|
|
||||||
|
wxMenu* menu = nullptr;
|
||||||
|
|
||||||
if (obj_idx == -1)
|
if (obj_idx == -1)
|
||||||
return;
|
menu = &default_menu;
|
||||||
|
else
|
||||||
wxMenu* menu = printer_technology == ptSLA ? &sla_object_menu :
|
|
||||||
get_selection().is_single_full_instance() ? // show "Object menu" for each FullInstance instead of FullObject
|
|
||||||
&object_menu : &part_menu;
|
|
||||||
|
|
||||||
sidebar->obj_list()->append_menu_item_settings(menu);
|
|
||||||
|
|
||||||
if (printer_technology != ptSLA)
|
|
||||||
sidebar->obj_list()->append_menu_item_change_extruder(menu);
|
|
||||||
|
|
||||||
if (menu != &part_menu)
|
|
||||||
{
|
{
|
||||||
/* Remove/Prepend "increase/decrease instances" menu items according to the view mode.
|
// If in 3DScene is(are) selected volume(s), but right button was clicked on empty space
|
||||||
* Suppress to show those items for a Simple mode
|
if (evt.data.second)
|
||||||
*/
|
return;
|
||||||
const MenuIdentifier id = printer_technology == ptSLA ? miObjectSLA : miObjectFFF;
|
|
||||||
if (wxGetApp().get_mode() == comSimple) {
|
menu = printer_technology == ptSLA ? &sla_object_menu :
|
||||||
if (menu->FindItem(_(L("Add instance"))) != wxNOT_FOUND)
|
get_selection().is_single_full_instance() ? // show "Object menu" for each FullInstance instead of FullObject
|
||||||
{
|
&object_menu : &part_menu;
|
||||||
/* Detach an items from the menu, but don't delete them
|
|
||||||
* so that they can be added back later
|
sidebar->obj_list()->append_menu_item_settings(menu);
|
||||||
* (after switching to the Advanced/Expert mode)
|
|
||||||
*/
|
if (printer_technology != ptSLA)
|
||||||
menu->Remove(items_increase[id]);
|
sidebar->obj_list()->append_menu_item_change_extruder(menu);
|
||||||
menu->Remove(items_decrease[id]);
|
|
||||||
menu->Remove(items_set_number_of_copies[id]);
|
if (menu != &part_menu)
|
||||||
|
{
|
||||||
|
/* Remove/Prepend "increase/decrease instances" menu items according to the view mode.
|
||||||
|
* Suppress to show those items for a Simple mode
|
||||||
|
*/
|
||||||
|
const MenuIdentifier id = printer_technology == ptSLA ? miObjectSLA : miObjectFFF;
|
||||||
|
if (wxGetApp().get_mode() == comSimple) {
|
||||||
|
if (menu->FindItem(_(L("Add instance"))) != wxNOT_FOUND)
|
||||||
|
{
|
||||||
|
/* Detach an items from the menu, but don't delete them
|
||||||
|
* so that they can be added back later
|
||||||
|
* (after switching to the Advanced/Expert mode)
|
||||||
|
*/
|
||||||
|
menu->Remove(items_increase[id]);
|
||||||
|
menu->Remove(items_decrease[id]);
|
||||||
|
menu->Remove(items_set_number_of_copies[id]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
else {
|
if (menu->FindItem(_(L("Add instance"))) == wxNOT_FOUND)
|
||||||
if (menu->FindItem(_(L("Add instance"))) == wxNOT_FOUND)
|
{
|
||||||
{
|
// Prepend items to the menu, if those aren't not there
|
||||||
// Prepend items to the menu, if those aren't not there
|
menu->Prepend(items_set_number_of_copies[id]);
|
||||||
menu->Prepend(items_set_number_of_copies[id]);
|
menu->Prepend(items_decrease[id]);
|
||||||
menu->Prepend(items_decrease[id]);
|
menu->Prepend(items_increase[id]);
|
||||||
menu->Prepend(items_increase[id]);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (q != nullptr) {
|
if (q != nullptr && menu) {
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
// For some reason on Linux the menu isn't displayed if position is specified
|
// For some reason on Linux the menu isn't displayed if position is specified
|
||||||
// (even though the position is sane).
|
// (even though the position is sane).
|
||||||
q->PopupMenu(menu);
|
q->PopupMenu(menu);
|
||||||
#else
|
#else
|
||||||
q->PopupMenu(menu, (int)evt.data.x(), (int)evt.data.y());
|
q->PopupMenu(menu, (int)evt.data.first.x(), (int)evt.data.first.y());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3548,12 +3559,14 @@ bool Plater::priv::init_object_menu()
|
|||||||
init_common_menu(&part_menu, true);
|
init_common_menu(&part_menu, true);
|
||||||
complit_init_part_menu();
|
complit_init_part_menu();
|
||||||
|
|
||||||
|
sidebar->obj_list()->create_default_popupmenu(&default_menu);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::priv::msw_rescale_object_menu()
|
void Plater::priv::msw_rescale_object_menu()
|
||||||
{
|
{
|
||||||
for (MenuWithSeparators* menu : { &object_menu, &sla_object_menu, &part_menu })
|
for (MenuWithSeparators* menu : { &object_menu, &sla_object_menu, &part_menu, &default_menu })
|
||||||
msw_rescale_menu(dynamic_cast<wxMenu*>(menu));
|
msw_rescale_menu(dynamic_cast<wxMenu*>(menu));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4323,14 +4336,14 @@ void Plater::set_number_of_copies(/*size_t num*/)
|
|||||||
|
|
||||||
ModelObject* model_object = p->model.objects[obj_idx];
|
ModelObject* model_object = p->model.objects[obj_idx];
|
||||||
|
|
||||||
const auto num = wxGetNumberFromUser( " ", _("Enter the number of copies:"),
|
const int num = wxGetNumberFromUser( " ", _("Enter the number of copies:"),
|
||||||
_("Copies of the selected object"), model_object->instances.size(), 0, 1000, this );
|
_("Copies of the selected object"), model_object->instances.size(), 0, 1000, this );
|
||||||
if (num < 0)
|
if (num < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Plater::TakeSnapshot snapshot(this, wxString::Format(_(L("Set numbers of copies to %d")), num));
|
Plater::TakeSnapshot snapshot(this, wxString::Format(_(L("Set numbers of copies to %d")), num));
|
||||||
|
|
||||||
int diff = (int)num - (int)model_object->instances.size();
|
int diff = num - (int)model_object->instances.size();
|
||||||
if (diff > 0)
|
if (diff > 0)
|
||||||
increase_instances(diff);
|
increase_instances(diff);
|
||||||
else if (diff < 0)
|
else if (diff < 0)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user