Merge remote-tracking branch 'origin' into ys_manipulation_panel_rw

This commit is contained in:
YuSanka 2019-09-23 14:37:06 +02:00
commit d90a27d5f8
18 changed files with 287 additions and 232 deletions

View File

@ -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:

View File

@ -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.

View File

@ -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}"

View File

@ -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

View File

@ -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) {
free(hstr); /* Success, we are connected */
break;
if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { }
avrdude_message(MSG_INFO, "%s: net_open(): Cannot open socket: %s\n", close(fd);
}
if (rp == NULL) {
avrdude_message(MSG_INFO, "%s: net_open(): Cannot connect: %s\n",
progname, strerror(errno)); progname, strerror(errno));
return -1;
} }
else {
memset(&sockaddr, 0, sizeof(struct sockaddr_in));
sockaddr.sin_family = AF_INET;
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;
}
fdp->ifd = fd; fdp->ifd = fd;
return 0; ret = 0;
}
freeaddrinfo(result);
error:
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 */
} }

View File

@ -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;
try {
ClipperOffset offs;
offs.AddPath(sh.Contour, jtMiter, etClosedPolygon); offs.AddPath(sh.Contour, jtMiter, etClosedPolygon);
offs.AddPaths(sh.Holes, jtMiter, etClosedPolygon); offs.AddPaths(sh.Holes, jtMiter, etClosedPolygon);
offs.Execute(result, static_cast<double>(distance)); 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.

View File

@ -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>

View File

@ -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);
} }

View File

@ -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++;
} }
} }

View File

@ -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) {
std::vector<Item> input = prusaParts();
libnest2d::nest(input, Box(250000000, 210000000), [](unsigned cnt) { // Get the input items and define the bin.
std::cout << "parts left: " << cnt << std::endl; std::vector<Item> input = prusaParts();
auto bin = Box(250000000, 210000000);
// 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; auto bins = size_t(max_binid_it == input.end() ? 0 :
max_binid_it->binId() + 1);
ASSERT_EQ(bins, 2u); // 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);
} }

View File

@ -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));
if (!clpath.Contour.empty()) {
auto firstp = clpath.Contour.front(); auto firstp = clpath.Contour.front();
clpath.Contour.emplace_back(firstp); 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);

View File

@ -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:

View File

@ -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];

View File

@ -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")) + ": ";

View File

@ -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>(); Vec2d logical_pos = pos.cast<double>();
#if ENABLE_RETINA_GL #if ENABLE_RETINA_GL
const float factor = m_retina_helper->get_scale_factor(); const float factor = m_retina_helper->get_scale_factor();
logical_pos = logical_pos.cwiseQuotient(Vec2d(factor, factor)); logical_pos = logical_pos.cwiseQuotient(Vec2d(factor, factor));
#endif // ENABLE_RETINA_GL #endif // ENABLE_RETINA_GL
post_event(Vec2dEvent(EVT_GLCANVAS_RIGHT_CLICK, logical_pos)); 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;
} }

View File

@ -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);

View File

@ -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) {
if (wxOSX)
UnselectAll(); 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;
@ -821,14 +837,20 @@ void ObjectList::list_manipulation(bool evt_context_menu/* = false*/)
else if (title == _("Editing")) else if (title == _("Editing"))
show_context_menu(evt_context_menu); show_context_menu(evt_context_menu);
else if (title == _("Name")) else if (title == _("Name"))
{
if (wxOSX)
show_context_menu(evt_context_menu); // return context menu under OSX (related to #2909)
if (is_windows10())
{ {
int obj_idx, vol_idx; int obj_idx, vol_idx;
get_selected_item_indexes(obj_idx, vol_idx, item); get_selected_item_indexes(obj_idx, vol_idx, item);
if (is_windows10() && get_mesh_errors_count(obj_idx, vol_idx) > 0 && if (get_mesh_errors_count(obj_idx, vol_idx) > 0 &&
pt.x > 2*wxGetApp().em_unit() && pt.x < 4*wxGetApp().em_unit() ) pt.x > 2*wxGetApp().em_unit() && pt.x < 4*wxGetApp().em_unit() )
fix_through_netfabb(); fix_through_netfabb();
} }
}
#ifndef __WXMSW__ #ifndef __WXMSW__
GetMainWindow()->SetToolTip(""); // hide tooltip GetMainWindow()->SetToolTip(""); // hide tooltip
@ -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();

View File

@ -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,13 +3448,21 @@ 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)
menu = &default_menu;
else
{
// If in 3DScene is(are) selected volume(s), but right button was clicked on empty space
if (evt.data.second)
return; return;
wxMenu* menu = printer_technology == ptSLA ? &sla_object_menu : menu = printer_technology == ptSLA ? &sla_object_menu :
get_selection().is_single_full_instance() ? // show "Object menu" for each FullInstance instead of FullObject get_selection().is_single_full_instance() ? // show "Object menu" for each FullInstance instead of FullObject
&object_menu : &part_menu; &object_menu : &part_menu;
@ -3489,14 +3499,15 @@ void Plater::priv::on_right_click(Vec2dEvent& evt)
} }
} }
} }
}
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)