Merge branch 'master' of https://github.com/Prusa3d/PrusaSlicer
This commit is contained in:
commit
4b35ebe6e5
49 changed files with 360 additions and 347 deletions
|
@ -89,33 +89,34 @@ struct stl_neighbors {
|
|||
};
|
||||
|
||||
struct stl_stats {
|
||||
stl_stats() { this->reset(); }
|
||||
void reset() { memset(this, 0, sizeof(stl_stats)); this->volume = -1.0; }
|
||||
char header[81];
|
||||
stl_type type;
|
||||
uint32_t number_of_facets;
|
||||
stl_vertex max;
|
||||
stl_vertex min;
|
||||
stl_vertex size;
|
||||
float bounding_diameter;
|
||||
float shortest_edge;
|
||||
float volume;
|
||||
int connected_edges;
|
||||
int connected_facets_1_edge;
|
||||
int connected_facets_2_edge;
|
||||
int connected_facets_3_edge;
|
||||
int facets_w_1_bad_edge;
|
||||
int facets_w_2_bad_edge;
|
||||
int facets_w_3_bad_edge;
|
||||
int original_num_facets;
|
||||
int edges_fixed;
|
||||
int degenerate_facets;
|
||||
int facets_removed;
|
||||
int facets_added;
|
||||
int facets_reversed;
|
||||
int backwards_edges;
|
||||
int normals_fixed;
|
||||
int number_of_parts;
|
||||
stl_stats() { memset(&header, 0, 81); }
|
||||
char header[81] = "";
|
||||
stl_type type = (stl_type)0;
|
||||
uint32_t number_of_facets = 0;
|
||||
stl_vertex max = stl_vertex::Zero();
|
||||
stl_vertex min = stl_vertex::Zero();
|
||||
stl_vertex size = stl_vertex::Zero();
|
||||
float bounding_diameter = 0.f;
|
||||
float shortest_edge = 0.f;
|
||||
float volume = -1.f;
|
||||
int connected_edges = 0;
|
||||
int connected_facets_1_edge = 0;
|
||||
int connected_facets_2_edge = 0;
|
||||
int connected_facets_3_edge = 0;
|
||||
int facets_w_1_bad_edge = 0;
|
||||
int facets_w_2_bad_edge = 0;
|
||||
int facets_w_3_bad_edge = 0;
|
||||
int original_num_facets = 0;
|
||||
int edges_fixed = 0;
|
||||
int degenerate_facets = 0;
|
||||
int facets_removed = 0;
|
||||
int facets_added = 0;
|
||||
int facets_reversed = 0;
|
||||
int backwards_edges = 0;
|
||||
int normals_fixed = 0;
|
||||
int number_of_parts = 0;
|
||||
|
||||
void clear() { *this = stl_stats(); }
|
||||
};
|
||||
|
||||
struct stl_file {
|
||||
|
@ -124,7 +125,7 @@ struct stl_file {
|
|||
void clear() {
|
||||
this->facet_start.clear();
|
||||
this->neighbors_start.clear();
|
||||
this->stats.reset();
|
||||
this->stats.clear();
|
||||
}
|
||||
|
||||
size_t memsize() const {
|
||||
|
|
|
@ -36,6 +36,10 @@
|
|||
#error "SEEK_SET not defined"
|
||||
#endif
|
||||
|
||||
#ifndef BOOST_LITTLE_ENDIAN
|
||||
extern void stl_internal_reverse_quads(char *buf, size_t cnt);
|
||||
#endif /* BOOST_LITTLE_ENDIAN */
|
||||
|
||||
static FILE* stl_open_count_facets(stl_file *stl, const char *file)
|
||||
{
|
||||
// Open the file in binary mode first.
|
||||
|
@ -238,10 +242,6 @@ bool stl_open(stl_file *stl, const char *file)
|
|||
return result;
|
||||
}
|
||||
|
||||
#ifndef BOOST_LITTLE_ENDIAN
|
||||
extern void stl_internal_reverse_quads(char *buf, size_t cnt);
|
||||
#endif /* BOOST_LITTLE_ENDIAN */
|
||||
|
||||
void stl_allocate(stl_file *stl)
|
||||
{
|
||||
// Allocate memory for the entire .STL 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>
|
||||
|
||||
Submitted by Hannes Jochriem:
|
||||
|
|
|
@ -505,12 +505,19 @@ network connection to (TCP)
|
|||
on
|
||||
.Ar host
|
||||
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
|
||||
that connects the network stream to a local serial port where the
|
||||
actual programmer has been attached to.
|
||||
The port is assumed to be properly configured, for example using a
|
||||
transparent 8-bit data connection without parity at 115200 Baud
|
||||
for a STK500.
|
||||
.Pp
|
||||
Note: The ability to handle IPv6 hostnames and addresses is limited to
|
||||
Posix systems (by now).
|
||||
.It Fl q
|
||||
Disable (or quell) output of the progress bar while reading or writing
|
||||
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])
|
||||
|
||||
# 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])
|
||||
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
|
||||
network connection to (TCP) @var{port} on @var{host}
|
||||
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
|
||||
that connects the network stream to a local serial port where the
|
||||
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
|
||||
for a STK500.
|
||||
|
||||
Note: The ability to handle IPv6 hostnames and addresses is limited to
|
||||
Posix systems (by now).
|
||||
|
||||
@item -q
|
||||
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;
|
||||
}
|
||||
|
||||
#include "ac_cfg.h"
|
||||
|
||||
// Timeout read & write variants
|
||||
// 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
|
||||
net_open(const char *port, union filedescriptor *fdp)
|
||||
{
|
||||
char *hstr, *pstr, *end;
|
||||
unsigned int pnum;
|
||||
int fd;
|
||||
struct sockaddr_in sockaddr;
|
||||
struct hostent *hp;
|
||||
#ifdef HAVE_GETADDRINFO
|
||||
char *hp, *hstr, *pstr;
|
||||
int s, fd, ret = -1;
|
||||
struct addrinfo hints;
|
||||
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",
|
||||
progname);
|
||||
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",
|
||||
progname, hstr);
|
||||
free(hstr);
|
||||
return -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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';
|
||||
|
||||
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)) {
|
||||
avrdude_message(MSG_INFO, "%s: net_open(): Bad port number \"%s\"\n",
|
||||
progname, pstr);
|
||||
free(hstr);
|
||||
return -1;
|
||||
if (s != 0) {
|
||||
avrdude_message(MSG_INFO,
|
||||
"%s: net_open(): Cannot resolve "
|
||||
"host=\"%s\", port=\"%s\": %s\n",
|
||||
progname, hstr, pstr, gai_strerror(s));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((hp = gethostbyname(hstr)) == NULL) {
|
||||
avrdude_message(MSG_INFO, "%s: net_open(): unknown host \"%s\"\n",
|
||||
progname, hstr);
|
||||
free(hstr);
|
||||
return -1;
|
||||
for (rp = result; rp != NULL; rp = rp->ai_next) {
|
||||
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (fd == -1) {
|
||||
/* This one failed, loop over */
|
||||
continue;
|
||||
}
|
||||
if (connect(fd, rp->ai_addr, rp->ai_addrlen) != -1) {
|
||||
/* Success, we are connected */
|
||||
break;
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
free(hstr);
|
||||
|
||||
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;
|
||||
if (rp == NULL) {
|
||||
avrdude_message(MSG_INFO, "%s: net_open(): Cannot connect: %s\n",
|
||||
progname, strerror(errno));
|
||||
}
|
||||
|
||||
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;
|
||||
else {
|
||||
fdp->ifd = fd;
|
||||
ret = 0;
|
||||
}
|
||||
freeaddrinfo(result);
|
||||
|
||||
fdp->ifd = fd;
|
||||
return 0;
|
||||
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 */
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -15,4 +15,4 @@
|
|||
#undef clipper_hpp
|
||||
#undef use_xyz
|
||||
|
||||
#endif clipper_z_hpp
|
||||
#endif // clipper_z_hpp
|
||||
|
|
|
@ -81,17 +81,16 @@ inline void offset(PolygonImpl& sh, TCoord<PointImpl> distance, const PolygonTag
|
|||
using ClipperLib::etClosedPolygon;
|
||||
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;
|
||||
offs.AddPath(sh.Contour, jtMiter, etClosedPolygon);
|
||||
offs.AddPaths(sh.Holes, jtMiter, etClosedPolygon);
|
||||
offs.Execute(result, static_cast<double>(distance));
|
||||
|
||||
try {
|
||||
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
|
||||
// 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 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>
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
|
||||
#include <cassert>
|
||||
|
||||
// For caching nfps
|
||||
#include <unordered_map>
|
||||
|
||||
// For parallel for
|
||||
#include <functional>
|
||||
#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 {
|
||||
|
||||
template<class RawShape>
|
||||
|
@ -529,17 +477,9 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin
|
|||
|
||||
using MaxNfpLevel = nfp::MaxNfpLevel<RawShape>;
|
||||
|
||||
using ItemKeys = std::vector<__itemhash::Key>;
|
||||
|
||||
// Norming factor for the optimization function
|
||||
const double norm_;
|
||||
|
||||
// Caching calculated nfps
|
||||
__itemhash::Hash<RawShape> nfpcache_;
|
||||
|
||||
// Storing item hash keys
|
||||
ItemKeys item_keys_;
|
||||
|
||||
public:
|
||||
|
||||
using Pile = nfp::Shapes<RawShape>;
|
||||
|
@ -636,15 +576,12 @@ public:
|
|||
private:
|
||||
|
||||
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;
|
||||
|
||||
Shapes nfps(items_.size());
|
||||
const Item& trsh = itsh.first;
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////
|
||||
// TODO: this is a workaround and should be solved in Item with mutexes
|
||||
|
@ -678,12 +615,11 @@ private:
|
|||
|
||||
|
||||
template<class Level>
|
||||
Shapes calcnfp( const ItemWithHash itsh, Level)
|
||||
Shapes calcnfp(const Item &trsh, Level)
|
||||
{ // Function for arbitrary level of nfp implementation
|
||||
using namespace nfp;
|
||||
|
||||
Shapes nfps;
|
||||
const Item& trsh = itsh.first;
|
||||
|
||||
auto& orb = trsh.transformedShape();
|
||||
bool orbconvex = trsh.isContourConvex();
|
||||
|
@ -849,8 +785,6 @@ private:
|
|||
remlist.insert(remlist.end(), remaining.from, remaining.to);
|
||||
}
|
||||
|
||||
size_t itemhash = __itemhash::hash(item);
|
||||
|
||||
if(items_.empty()) {
|
||||
setInitialPosition(item);
|
||||
best_overfit = overfit(item.transformedShape(), bin_);
|
||||
|
@ -875,7 +809,7 @@ private:
|
|||
// it is disjunct from the current merged pile
|
||||
placeOutsideOfBin(item);
|
||||
|
||||
nfps = calcnfp({item, itemhash}, Lvl<MaxNfpLevel::value>());
|
||||
nfps = calcnfp(item, Lvl<MaxNfpLevel::value>());
|
||||
|
||||
auto iv = item.referenceVertex();
|
||||
|
||||
|
@ -1112,7 +1046,6 @@ private:
|
|||
|
||||
if(can_pack) {
|
||||
ret = PackResult(item);
|
||||
item_keys_.emplace_back(itemhash);
|
||||
} else {
|
||||
ret = PackResult(best_overfit);
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ protected:
|
|||
|
||||
Placer p{bin};
|
||||
p.configure(pcfg);
|
||||
if (!p.pack(cpy)) it = c.erase(it);
|
||||
if (itm.area() <= 0 || !p.pack(cpy)) it = c.erase(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;
|
||||
|
||||
if(ret.empty()) {
|
||||
|
@ -51,7 +51,7 @@ std::vector<libnest2d::Item>& prusaParts() {
|
|||
return ret;
|
||||
}
|
||||
|
||||
TEST(BasicFunctionality, Angles)
|
||||
TEST(GeometryAlgorithms, Angles)
|
||||
{
|
||||
|
||||
using namespace libnest2d;
|
||||
|
@ -109,7 +109,7 @@ TEST(BasicFunctionality, Angles)
|
|||
}
|
||||
|
||||
// Simple test, does not use gmock
|
||||
TEST(BasicFunctionality, creationAndDestruction)
|
||||
TEST(Nesting, ItemCreationAndDestruction)
|
||||
{
|
||||
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();
|
||||
|
||||
libnest2d::nest(input, Box(250000000, 210000000), [](unsigned cnt) {
|
||||
std::cout << "parts left: " << cnt << std::endl;
|
||||
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(),
|
||||
[](const Item &i1, const Item &i2) {
|
||||
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(
|
||||
std::all_of(input.begin(), input.end(), [](const Item &itm) {
|
||||
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 {
|
||||
|
@ -966,26 +1014,20 @@ using Ratio = boost::rational<boost::multiprecision::int128_t>;
|
|||
|
||||
}
|
||||
|
||||
TEST(RotatingCalipers, MinAreaBBCClk) {
|
||||
auto u = [](ClipperLib::cInt n) { return n*1000000; };
|
||||
PolygonImpl poly({ {u(0), u(0)}, {u(4), u(1)}, {u(2), u(4)}});
|
||||
//TEST(GeometryAlgorithms, MinAreaBBCClk) {
|
||||
// auto u = [](ClipperLib::cInt n) { return n*1000000; };
|
||||
// PolygonImpl poly({ {u(0), u(0)}, {u(4), u(1)}, {u(2), u(4)}});
|
||||
|
||||
long double arearef = refMinAreaBox(poly);
|
||||
long double area = minAreaBoundingBox<PolygonImpl, Unit, Ratio>(poly).area();
|
||||
// long double arearef = refMinAreaBox(poly);
|
||||
// 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) {
|
||||
// /size_t idx = 0;
|
||||
TEST(GeometryAlgorithms, MinAreaBBWithRotatingCalipers) {
|
||||
long double err_epsilon = 500e6l;
|
||||
|
||||
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);
|
||||
|
||||
long double arearef = refMinAreaBox(poly);
|
||||
|
@ -993,8 +1035,6 @@ TEST(RotatingCalipers, AllPrusaMinBB) {
|
|||
long double area = cast<long double>(bb.area());
|
||||
|
||||
bool succ = std::abs(arearef - area) < err_epsilon;
|
||||
// std::cout << idx++ << " " << (succ? "ok" : "failed") << " ref: "
|
||||
// << arearef << " actual: " << area << std::endl;
|
||||
|
||||
ASSERT_TRUE(succ);
|
||||
}
|
||||
|
@ -1011,8 +1051,6 @@ TEST(RotatingCalipers, AllPrusaMinBB) {
|
|||
|
||||
|
||||
bool succ = std::abs(arearef - area) < err_epsilon;
|
||||
// std::cout << idx++ << " " << (succ? "ok" : "failed") << " ref: "
|
||||
// << arearef << " actual: " << area << std::endl;
|
||||
|
||||
ASSERT_TRUE(succ);
|
||||
}
|
||||
|
|
|
@ -618,19 +618,21 @@ void arrange(ArrangePolygons & arrangables,
|
|||
items.reserve(arrangables.size());
|
||||
|
||||
// Create Item from Arrangeable
|
||||
auto process_arrangeable =
|
||||
[](const ArrangePolygon &arrpoly, std::vector<Item> &outp)
|
||||
auto process_arrangeable = [](const ArrangePolygon &arrpoly,
|
||||
std::vector<Item> & outp)
|
||||
{
|
||||
Polygon p = arrpoly.poly.contour;
|
||||
const Vec2crd & offs = arrpoly.translation;
|
||||
double rotation = arrpoly.rotation;
|
||||
Polygon p = arrpoly.poly.contour;
|
||||
const Vec2crd &offs = arrpoly.translation;
|
||||
double rotation = arrpoly.rotation;
|
||||
|
||||
if (p.is_counter_clockwise()) p.reverse();
|
||||
|
||||
clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p));
|
||||
|
||||
auto firstp = clpath.Contour.front();
|
||||
clpath.Contour.emplace_back(firstp);
|
||||
|
||||
if (!clpath.Contour.empty()) {
|
||||
auto firstp = clpath.Contour.front();
|
||||
clpath.Contour.emplace_back(firstp);
|
||||
}
|
||||
|
||||
outp.emplace_back(std::move(clpath));
|
||||
outp.back().rotation(rotation);
|
||||
|
|
|
@ -249,7 +249,7 @@ ConfigOption* ConfigOptionDef::create_default_option() const
|
|||
// Special case: For a DynamicConfig, convert a templated enum to a generic enum.
|
||||
new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->getInt()) :
|
||||
this->default_value->clone();
|
||||
return this->create_empty_option();
|
||||
return this->create_empty_option();
|
||||
}
|
||||
|
||||
// Assignment of the serialization IDs is not thread safe. The Defs shall be initialized from the main thread!
|
||||
|
|
|
@ -353,7 +353,7 @@ public:
|
|||
bool apply_override(const ConfigOption *rhs) override {
|
||||
if (this->nullable())
|
||||
throw std::runtime_error("Cannot override a nullable ConfigOption.");
|
||||
if (rhs->type() != this->type())
|
||||
if (rhs->type() != this->type())
|
||||
throw std::runtime_error("ConfigOptionVector.apply_override() applied to different types.");
|
||||
auto rhs_vec = static_cast<const ConfigOptionVector<T>*>(rhs);
|
||||
if (! rhs->nullable()) {
|
||||
|
@ -461,7 +461,7 @@ public:
|
|||
for (const double &v : this->values) {
|
||||
if (&v != &this->values.front())
|
||||
ss << ",";
|
||||
serialize_single_value(ss, v);
|
||||
serialize_single_value(ss, v);
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
@ -607,7 +607,7 @@ public:
|
|||
for (const int &v : this->values) {
|
||||
if (&v != &this->values.front())
|
||||
ss << ",";
|
||||
serialize_single_value(ss, v);
|
||||
serialize_single_value(ss, v);
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
|
|
@ -15,40 +15,39 @@ namespace Slic3r {
|
|||
|
||||
struct SurfaceFillParams
|
||||
{
|
||||
SurfaceFillParams() : flow(0.f, 0.f, 0.f, false) { memset(this, 0, sizeof(*this)); }
|
||||
// Zero based extruder ID.
|
||||
unsigned int extruder;
|
||||
unsigned int extruder = 0;
|
||||
// Infill pattern, adjusted for the density etc.
|
||||
InfillPattern pattern;
|
||||
InfillPattern pattern = InfillPattern(0);
|
||||
|
||||
// FillBase
|
||||
// in unscaled coordinates
|
||||
coordf_t spacing;
|
||||
coordf_t spacing = 0.;
|
||||
// infill / perimeter overlap, in unscaled coordinates
|
||||
coordf_t overlap;
|
||||
coordf_t overlap = 0.;
|
||||
// Angle as provided by the region config, in radians.
|
||||
float angle;
|
||||
float angle = 0.f;
|
||||
// Non-negative for a bridge.
|
||||
float bridge_angle;
|
||||
float bridge_angle = 0.f;
|
||||
|
||||
// FillParams
|
||||
float density;
|
||||
float density = 0.f;
|
||||
// Don't connect the fill lines around the inner perimeter.
|
||||
bool dont_connect;
|
||||
bool dont_connect = false;
|
||||
// Don't adjust spacing to fill the space evenly.
|
||||
bool dont_adjust;
|
||||
bool dont_adjust = false;
|
||||
|
||||
// width, height of extrusion, nozzle diameter, is bridge
|
||||
// For the output, for fill generator.
|
||||
Flow flow;
|
||||
Flow flow = Flow(0.f, 0.f, 0.f, false);
|
||||
|
||||
// For the output
|
||||
ExtrusionRole extrusion_role;
|
||||
ExtrusionRole extrusion_role = ExtrusionRole(0);
|
||||
|
||||
// Various print settings?
|
||||
|
||||
// Index of this entry in a linear vector.
|
||||
size_t idx;
|
||||
size_t idx = 0;
|
||||
|
||||
|
||||
bool operator<(const SurfaceFillParams &rhs) const {
|
||||
|
|
|
@ -979,7 +979,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
|
|||
stream << layer_height_profile.front();
|
||||
for (size_t i = 1; i < layer_height_profile.size(); ++i)
|
||||
stream << ";" << layer_height_profile[i];
|
||||
stream << "\n </metadata>\n";
|
||||
stream << "\n </metadata>\n";
|
||||
}
|
||||
|
||||
// Export layer height ranges including the layer range specific config overrides.
|
||||
|
|
|
@ -246,7 +246,7 @@ static void extract_model_from_archive(
|
|||
sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) {
|
||||
// Normal was mangled. Maybe denormals or "not a number" were stored?
|
||||
// Just reset the normal and silently ignore it.
|
||||
memset(&facet.normal, 0, sizeof(facet.normal));
|
||||
facet.normal = stl_normal::Zero();
|
||||
}
|
||||
facets.emplace_back(facet);
|
||||
}
|
||||
|
@ -278,7 +278,7 @@ static void extract_model_from_archive(
|
|||
instance->set_rotation(instance_rotation);
|
||||
instance->set_scaling_factor(instance_scaling_factor);
|
||||
instance->set_offset(instance_offset);
|
||||
if (group_id != (size_t)-1)
|
||||
if (group_id != (unsigned int)(-1))
|
||||
group_to_model_object[group_id] = model_object;
|
||||
} else {
|
||||
// This is not the 1st mesh of a group. Add it to the ModelObject.
|
||||
|
|
|
@ -660,7 +660,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
|
|||
if (print->is_step_done(psGCodeExport) && boost::filesystem::exists(boost::filesystem::path(path)))
|
||||
return;
|
||||
|
||||
print->set_started(psGCodeExport);
|
||||
print->set_started(psGCodeExport);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Exporting G-code..." << log_memory_info();
|
||||
|
||||
|
@ -1412,7 +1412,7 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc
|
|||
// Skip the rest of the line.
|
||||
for (; *ptr != 0 && *ptr != '\r' && *ptr != '\n'; ++ ptr);
|
||||
// Skip the end of line indicators.
|
||||
for (; *ptr == '\r' || *ptr == '\n'; ++ ptr);
|
||||
for (; *ptr == '\r' || *ptr == '\n'; ++ ptr);
|
||||
}
|
||||
return temp_set_by_gcode;
|
||||
}
|
||||
|
|
|
@ -359,7 +359,7 @@ std::string GCodePreviewData::get_legend_title() const
|
|||
case Extrusion::Feedrate:
|
||||
return L("Speed (mm/s)");
|
||||
case Extrusion::VolumetricRate:
|
||||
return L("Volumetric flow rate (mm3/s)");
|
||||
return L("Volumetric flow rate (mm³/s)");
|
||||
case Extrusion::Tool:
|
||||
return L("Tool");
|
||||
case Extrusion::ColorPrint:
|
||||
|
|
|
@ -308,7 +308,7 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_
|
|||
LayerTools lt_new(0.5f * (lt.print_z + lt_object.print_z));
|
||||
// Find the 1st layer above lt_new.
|
||||
for (j = i + 1; j < m_layer_tools.size() && m_layer_tools[j].print_z < lt_new.print_z - EPSILON; ++ j);
|
||||
if (std::abs(m_layer_tools[j].print_z - lt_new.print_z) < EPSILON) {
|
||||
if (std::abs(m_layer_tools[j].print_z - lt_new.print_z) < EPSILON) {
|
||||
m_layer_tools[j].has_wipe_tower = true;
|
||||
} else {
|
||||
LayerTools <_extra = *m_layer_tools.insert(m_layer_tools.begin() + j, lt_new);
|
||||
|
|
|
@ -698,7 +698,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool last_in_lay
|
|||
writer.append(std::string("; material : " + (m_current_tool < m_filpar.size() ? m_filpar[m_current_tool].material : "(NONE)") + " -> " + m_filpar[tool].material + "\n").c_str())
|
||||
.append(";--------------------\n");
|
||||
|
||||
writer.speed_override_backup();
|
||||
writer.speed_override_backup();
|
||||
writer.speed_override(100);
|
||||
|
||||
Vec2f initial_position = cleaning_box.ld + Vec2f(0.f, m_depth_traversed);
|
||||
|
@ -748,7 +748,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool last_in_lay
|
|||
if (m_current_tool < m_used_filament_length.size())
|
||||
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||
|
||||
ToolChangeResult result;
|
||||
ToolChangeResult result;
|
||||
result.priming = false;
|
||||
result.initial_tool = int(old_tool);
|
||||
result.new_tool = int(m_current_tool);
|
||||
|
@ -806,7 +806,7 @@ WipeTower::ToolChangeResult WipeTower::toolchange_Brim(bool sideOnly, float y_of
|
|||
if (m_current_tool < m_used_filament_length.size())
|
||||
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||
|
||||
ToolChangeResult result;
|
||||
ToolChangeResult result;
|
||||
result.priming = false;
|
||||
result.initial_tool = int(old_tool);
|
||||
result.new_tool = int(m_current_tool);
|
||||
|
@ -1163,7 +1163,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer()
|
|||
writer.extrude(box.rd.x() - m_perimeter_width / 2.f, writer.y() + 0.5f * step);
|
||||
writer.extrude(box.ld.x() + m_perimeter_width / 2.f, writer.y());
|
||||
}
|
||||
writer.travel(box.rd.x()-m_perimeter_width/2.f,writer.y()); // wipe the nozzle
|
||||
writer.travel(box.rd.x()-m_perimeter_width/2.f,writer.y()); // wipe the nozzle
|
||||
}
|
||||
else { // Extrude a sparse infill to support the material to be printed above.
|
||||
const float dy = (fill_box.lu.y() - fill_box.ld.y() - m_perimeter_width);
|
||||
|
@ -1196,7 +1196,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer()
|
|||
if (m_current_tool < m_used_filament_length.size())
|
||||
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||
|
||||
ToolChangeResult result;
|
||||
ToolChangeResult result;
|
||||
result.priming = false;
|
||||
result.initial_tool = int(old_tool);
|
||||
result.new_tool = int(m_current_tool);
|
||||
|
|
|
@ -144,7 +144,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
|||
}
|
||||
if (! lower_layer_covered->empty())
|
||||
voids = diff(voids, *lower_layer_covered);
|
||||
fill_boundaries = diff(fill_boundaries, voids);
|
||||
fill_boundaries = diff(fill_boundaries, voids);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -473,4 +473,4 @@ void LayerRegion::export_region_fill_surfaces_to_svg_debug(const char *name) con
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1462,7 +1462,7 @@ stl_stats ModelObject::get_object_stl_stats() const
|
|||
return this->volumes[0]->mesh().stl.stats;
|
||||
|
||||
stl_stats full_stats;
|
||||
memset(&full_stats, 0, sizeof(stl_stats));
|
||||
full_stats.volume = 0.f;
|
||||
|
||||
// fill full_stats from all objet's meshes
|
||||
for (ModelVolume* volume : this->volumes)
|
||||
|
|
|
@ -253,7 +253,7 @@ bool Print::is_step_done(PrintObjectStep step) const
|
|||
{
|
||||
if (m_objects.empty())
|
||||
return false;
|
||||
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||
for (const PrintObject *object : m_objects)
|
||||
if (! object->is_step_done_unguarded(step))
|
||||
return false;
|
||||
|
|
|
@ -268,8 +268,7 @@ public:
|
|||
std::string text;
|
||||
// Bitmap of flags.
|
||||
enum FlagBits {
|
||||
DEFAULT,
|
||||
NO_RELOAD_SCENE = 0,
|
||||
DEFAULT = 0,
|
||||
RELOAD_SCENE = 1 << 1,
|
||||
RELOAD_SLA_SUPPORT_POINTS = 1 << 2,
|
||||
RELOAD_SLA_PREVIEW = 1 << 3,
|
||||
|
|
|
@ -1475,7 +1475,7 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full
|
|||
|
||||
if (object_max_z <= 0.f)
|
||||
object_max_z = (float)model_object.raw_bounding_box().size().z();
|
||||
return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders);
|
||||
return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders);
|
||||
}
|
||||
|
||||
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
|
||||
|
|
|
@ -783,7 +783,7 @@ namespace SupportMaterialInternal {
|
|||
for (const ExtrusionPath &ep : loop.paths)
|
||||
if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty())
|
||||
return ep.size() >= (ep.is_closed() ? 3 : 2);
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
static bool has_bridging_perimeters(const ExtrusionEntityCollection &perimeters)
|
||||
{
|
||||
|
@ -2125,7 +2125,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
|
|||
}
|
||||
// $layer->slices contains the full shape of layer, thus including
|
||||
// perimeter's width. $support contains the full shape of support
|
||||
// material, thus including the width of its foremost extrusion.
|
||||
// material, thus including the width of its foremost extrusion.
|
||||
// We leave a gap equal to a full extrusion width.
|
||||
support_layer.polygons = diff(support_layer.polygons, polygons_trimming);
|
||||
}
|
||||
|
@ -2934,20 +2934,13 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
|||
// Prepare fillers.
|
||||
SupportMaterialPattern support_pattern = m_object_config->support_material_pattern;
|
||||
bool with_sheath = m_object_config->support_material_with_sheath;
|
||||
InfillPattern infill_pattern;
|
||||
InfillPattern infill_pattern = (support_pattern == smpHoneycomb ? ipHoneycomb : ipRectilinear);
|
||||
std::vector<float> angles;
|
||||
angles.push_back(base_angle);
|
||||
switch (support_pattern) {
|
||||
case smpRectilinearGrid:
|
||||
|
||||
if (support_pattern == smpRectilinearGrid)
|
||||
angles.push_back(interface_angle);
|
||||
// fall through
|
||||
case smpRectilinear:
|
||||
infill_pattern = ipRectilinear;
|
||||
break;
|
||||
case smpHoneycomb:
|
||||
infill_pattern = ipHoneycomb;
|
||||
break;
|
||||
}
|
||||
|
||||
BoundingBox bbox_object(Point(-scale_(1.), -scale_(1.0)), Point(scale_(1.), scale_(1.)));
|
||||
|
||||
// const coordf_t link_max_length_factor = 3.;
|
||||
|
@ -3217,7 +3210,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
|||
density = 0.5f;
|
||||
flow = m_first_layer_flow;
|
||||
// use the proper spacing for first layer as we don't need to align
|
||||
// its pattern to the other layers
|
||||
// its pattern to the other layers
|
||||
//FIXME When paralellizing, each thread shall have its own copy of the fillers.
|
||||
filler->spacing = flow.spacing();
|
||||
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density));
|
||||
|
|
|
@ -342,7 +342,7 @@ static void copy_config_dir_single_level(const boost::filesystem::path &path_src
|
|||
! boost::filesystem::create_directory(path_dst))
|
||||
throw std::runtime_error(std::string("Slic3r was unable to create a directory at ") + path_dst.string());
|
||||
|
||||
for (auto &dir_entry : boost::filesystem::directory_iterator(path_src))
|
||||
for (auto &dir_entry : boost::filesystem::directory_iterator(path_src))
|
||||
if (Slic3r::is_ini_file(dir_entry))
|
||||
boost::filesystem::copy_file(dir_entry.path(), path_dst / dir_entry.path().filename(), boost::filesystem::copy_option::overwrite_if_exists);
|
||||
}
|
||||
|
@ -351,7 +351,7 @@ static void delete_existing_ini_files(const boost::filesystem::path &path)
|
|||
{
|
||||
if (! boost::filesystem::is_directory(path))
|
||||
return;
|
||||
for (auto &dir_entry : boost::filesystem::directory_iterator(path))
|
||||
for (auto &dir_entry : boost::filesystem::directory_iterator(path))
|
||||
if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini"))
|
||||
boost::filesystem::remove(dir_entry.path());
|
||||
}
|
||||
|
@ -378,7 +378,7 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot:
|
|||
sprintf(name, "filament_%u", i);
|
||||
if (! app_config.has("presets", name))
|
||||
break;
|
||||
snapshot.filaments.emplace_back(app_config.get("presets", name));
|
||||
snapshot.filaments.emplace_back(app_config.get("presets", name));
|
||||
}
|
||||
// Vendor specific config bundles and installed printers.
|
||||
for (const std::pair<std::string, std::map<std::string, std::set<std::string>>> &vendor : app_config.vendors()) {
|
||||
|
@ -417,7 +417,7 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot:
|
|||
// Backup the presets.
|
||||
for (const char *subdir : { "print", "filament", "printer", "vendor" })
|
||||
copy_config_dir_single_level(data_dir / subdir, snapshot_dir / subdir);
|
||||
snapshot.save_ini((snapshot_dir / "snapshot.ini").string());
|
||||
snapshot.save_ini((snapshot_dir / "snapshot.ini").string());
|
||||
assert(m_snapshots.empty() || m_snapshots.back().time_captured <= snapshot.time_captured);
|
||||
m_snapshots.emplace_back(std::move(snapshot));
|
||||
return m_snapshots.back();
|
||||
|
|
|
@ -227,9 +227,9 @@ size_t Index::load(const boost::filesystem::path &path)
|
|||
// End of semver or keyword.
|
||||
break;
|
||||
}
|
||||
if (*key_end != 0 && *key_end != ' ' && *key_end != '\t' && *key_end != '=')
|
||||
if (*key_end != 0 && *key_end != ' ' && *key_end != '\t' && *key_end != '=')
|
||||
throw file_parser_error("Invalid keyword or semantic version", path, idx_line);
|
||||
char *value = left_trim(key_end);
|
||||
char *value = left_trim(key_end);
|
||||
bool key_value_pair = *value == '=';
|
||||
if (key_value_pair)
|
||||
value = left_trim(value + 1);
|
||||
|
@ -245,11 +245,11 @@ size_t Index::load(const boost::filesystem::path &path)
|
|||
if (strcmp(key, "min_slic3r_version") == 0 || strcmp(key, "max_slic3r_version") == 0) {
|
||||
if (! svalue.empty())
|
||||
semver = Semver::parse(svalue);
|
||||
if (! semver)
|
||||
if (! semver)
|
||||
throw file_parser_error(std::string(key) + " must referece a valid semantic version", path, idx_line);
|
||||
if (strcmp(key, "min_slic3r_version") == 0)
|
||||
if (strcmp(key, "min_slic3r_version") == 0)
|
||||
ver.min_slic3r_version = *semver;
|
||||
else
|
||||
else
|
||||
ver.max_slic3r_version = *semver;
|
||||
} else {
|
||||
// Ignore unknown keys, as there may come new keys in the future.
|
||||
|
|
|
@ -455,7 +455,7 @@ void BedShapePanel::update_shape()
|
|||
else if (page_idx == SHAPE_CUSTOM)
|
||||
m_shape = m_loaded_shape;
|
||||
|
||||
update_preview();
|
||||
update_preview();
|
||||
}
|
||||
|
||||
// Loads an stl file, projects it to the XY plane and calculates a polygon.
|
||||
|
|
|
@ -1225,13 +1225,16 @@ bool ConfigWizard::run(PresetBundle *preset_bundle, const PresetUpdater *updater
|
|||
|
||||
const wxString& ConfigWizard::name(const bool from_menu/* = false*/)
|
||||
{
|
||||
// A different naming convention is used for the Wizard on Windows vs. OSX & GTK.
|
||||
// A different naming convention is used for the Wizard on Windows & GTK vs. OSX.
|
||||
// Note: Don't call _() macro here.
|
||||
// This function just return the current name according to the OS.
|
||||
// Translation is implemented inside GUI_App::add_config_menu()
|
||||
#if __APPLE__
|
||||
static const wxString config_wizard_name = _(L("Configuration Assistant"));
|
||||
static const wxString config_wizard_name_menu = _(L("Configuration &Assistant"));
|
||||
static const wxString config_wizard_name = L("Configuration Assistant");
|
||||
static const wxString config_wizard_name_menu = L("Configuration &Assistant");
|
||||
#else
|
||||
static const wxString config_wizard_name = _(L("Configuration Wizard"));
|
||||
static const wxString config_wizard_name_menu = _(L("Configuration &Wizard"));
|
||||
static const wxString config_wizard_name = L("Configuration Wizard");
|
||||
static const wxString config_wizard_name_menu = L("Configuration &Wizard");
|
||||
#endif
|
||||
return from_menu ? config_wizard_name_menu : config_wizard_name;
|
||||
}
|
||||
|
|
|
@ -928,8 +928,8 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c
|
|||
if (items_count > 1)
|
||||
m_original_height += (items_count - 1) * scaled_square_contour;
|
||||
|
||||
m_width = (int)next_highest_power_of_2((uint32_t)m_original_width);
|
||||
m_height = (int)next_highest_power_of_2((uint32_t)m_original_height);
|
||||
m_width = (int)next_highest_power_of_2((uint32_t)m_original_width);
|
||||
m_height = (int)next_highest_power_of_2((uint32_t)m_original_height);
|
||||
|
||||
// generates bitmap
|
||||
wxBitmap bitmap(m_width, m_height);
|
||||
|
@ -1882,7 +1882,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
if (m_reload_delayed)
|
||||
return;
|
||||
|
||||
bool update_object_list = false;
|
||||
bool update_object_list = false;
|
||||
|
||||
if (m_volumes.volumes != glvolumes_new)
|
||||
update_object_list = true;
|
||||
|
@ -3389,10 +3389,9 @@ void GLCanvas3D::handle_sidebar_focus_event(const std::string& opt_key, bool foc
|
|||
m_sidebar_field = focus_on ? opt_key : "";
|
||||
|
||||
if (!m_sidebar_field.empty())
|
||||
{
|
||||
m_gizmos.reset_all_states();
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void GLCanvas3D::handle_layers_data_focus_event(const t_layer_height_range range, const EditorType type)
|
||||
|
@ -5070,14 +5069,14 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
|
|||
for (const GCodePreviewData::Extrusion::Layer &layer : preview_data.extrusion.layers)
|
||||
for (const ExtrusionPath &path : layer.paths)
|
||||
++ num_paths_per_role[size_t(path.role())];
|
||||
std::vector<std::vector<float>> roles_values;
|
||||
std::vector<std::vector<float>> roles_values;
|
||||
roles_values.assign(size_t(erCount), std::vector<float>());
|
||||
for (size_t i = 0; i < roles_values.size(); ++ i)
|
||||
roles_values[i].reserve(num_paths_per_role[i]);
|
||||
for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers)
|
||||
for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers)
|
||||
for (const ExtrusionPath& path : layer.paths)
|
||||
roles_values[size_t(path.role())].emplace_back(Helper::path_filter(preview_data.extrusion.view_type, path));
|
||||
roles_filters.reserve(size_t(erCount));
|
||||
roles_filters.reserve(size_t(erCount));
|
||||
size_t num_buffers = 0;
|
||||
for (std::vector<float> &values : roles_values) {
|
||||
sort_remove_duplicates(values);
|
||||
|
|
|
@ -125,7 +125,7 @@ void config_wizard(int reason)
|
|||
if (! wxGetApp().check_unsaved_changes())
|
||||
return;
|
||||
|
||||
try {
|
||||
try {
|
||||
ConfigWizard wizard(nullptr, static_cast<ConfigWizard::RunReason>(reason));
|
||||
wizard.run(wxGetApp().preset_bundle, wxGetApp().preset_updater);
|
||||
}
|
||||
|
|
|
@ -725,7 +725,7 @@ bool GUI_App::load_language(wxString language, bool initial)
|
|||
#endif
|
||||
if (initial)
|
||||
message + "\n\nApplication will close.";
|
||||
wxMessageBox(message, "PrusaSlicer - Switching language failed", wxOK | wxICON_ERROR);
|
||||
wxMessageBox(message, "PrusaSlicer - Switching language failed", wxOK | wxICON_ERROR);
|
||||
if (initial)
|
||||
std::exit(EXIT_FAILURE);
|
||||
else
|
||||
|
|
|
@ -273,6 +273,8 @@ void ObjectList::create_objects_ctrl()
|
|||
AppendBitmapColumn(_(L("Editing")), colEditing, wxDATAVIEW_CELL_INERT, 3*em,
|
||||
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);
|
||||
|
@ -751,9 +753,9 @@ void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& vol
|
|||
}
|
||||
|
||||
select_items(items);
|
||||
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||
//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||
selection_changed();
|
||||
#endif //no __WXOSX__ //__WXMSW__
|
||||
//#endif //no __WXOSX__ //__WXMSW__
|
||||
}
|
||||
|
||||
void ObjectList::paste_objects_into_list(const std::vector<size_t>& object_idxs)
|
||||
|
@ -771,9 +773,9 @@ void ObjectList::paste_objects_into_list(const std::vector<size_t>& object_idxs)
|
|||
wxGetApp().plater()->changed_objects(object_idxs);
|
||||
|
||||
select_items(items);
|
||||
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||
//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||
selection_changed();
|
||||
#endif //no __WXOSX__ //__WXMSW__
|
||||
//#endif //no __WXOSX__ //__WXMSW__
|
||||
}
|
||||
|
||||
#ifdef __WXOSX__
|
||||
|
@ -810,8 +812,15 @@ void ObjectList::list_manipulation(bool evt_context_menu/* = false*/)
|
|||
*/
|
||||
|
||||
if (!item) {
|
||||
if (wxOSX && col == nullptr)
|
||||
UnselectAll();
|
||||
if (col == nullptr) {
|
||||
if (wxOSX)
|
||||
UnselectAll();
|
||||
else if (!evt_context_menu)
|
||||
// Case, when last item was deleted and under GTK was called wxEVT_DATAVIEW_SELECTION_CHANGED,
|
||||
// which invoked next list_manipulation(false)
|
||||
return;
|
||||
}
|
||||
|
||||
if (evt_context_menu) {
|
||||
show_context_menu(evt_context_menu);
|
||||
return;
|
||||
|
@ -1330,7 +1339,7 @@ wxMenu* ObjectList::append_submenu_add_generic(wxMenu* menu, const ModelVolumeTy
|
|||
|
||||
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;
|
||||
append_menu_item(sub_menu, wxID_ANY, _(item), "",
|
||||
[this, type, item](wxCommandEvent&) { load_generic_subobject(item, type); }, "", menu);
|
||||
|
@ -1713,9 +1722,9 @@ void ObjectList::load_subobject(ModelVolumeType type)
|
|||
if (sel_item)
|
||||
select_item(sel_item);
|
||||
|
||||
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||
//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||
selection_changed();
|
||||
#endif //no __WXOSX__ //__WXMSW__
|
||||
//#endif //no __WXOSX__ //__WXMSW__
|
||||
}
|
||||
|
||||
void ObjectList::load_part( ModelObject* model_object,
|
||||
|
@ -1851,9 +1860,9 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
|
|||
const auto object_item = m_objects_model->GetTopParent(GetSelection());
|
||||
select_item(m_objects_model->AddVolumeChild(object_item, name, type,
|
||||
new_volume->get_mesh_errors_count()>0));
|
||||
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||
//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||
selection_changed();
|
||||
#endif //no __WXOSX__ //__WXMSW__
|
||||
//#endif //no __WXOSX__ //__WXMSW__
|
||||
}
|
||||
|
||||
void ObjectList::load_shape_object(const std::string& type_name)
|
||||
|
@ -1892,6 +1901,9 @@ void ObjectList::load_shape_object(const std::string& type_name)
|
|||
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
|
||||
new_object->invalidate_bounding_box();
|
||||
|
||||
new_object->center_around_origin();
|
||||
new_object->ensure_on_bed();
|
||||
|
||||
const BoundingBoxf bed_shape = wxGetApp().plater()->bed_shape_bb();
|
||||
new_object->instances[0]->set_offset(Slic3r::to_3d(bed_shape.center().cast<double>(), -new_object->origin_translation(2)));
|
||||
|
||||
|
@ -3677,10 +3689,10 @@ void ObjectList::msw_rescale()
|
|||
// update min size !!! A width of control shouldn't be a wxDefaultCoord
|
||||
SetMinSize(wxSize(1, 15 * em));
|
||||
|
||||
GetColumn(colName)->SetWidth(19 * em);
|
||||
GetColumn(colPrint)->SetWidth( 2 * em);
|
||||
GetColumn(colName )->SetWidth(20 * em);
|
||||
GetColumn(colPrint )->SetWidth( 3 * em);
|
||||
GetColumn(colExtruder)->SetWidth( 8 * em);
|
||||
GetColumn(colEditing)->SetWidth( 2 * em);
|
||||
GetColumn(colEditing )->SetWidth( 3 * em);
|
||||
|
||||
// rescale all icons, used by ObjectList
|
||||
msw_rescale_icons();
|
||||
|
|
|
@ -252,7 +252,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
const sla::SupportPoint& support_point = m_editing_mode ? m_editing_cache[i].support_point : m_normal_cache[i];
|
||||
const bool& point_selected = m_editing_mode ? m_editing_cache[i].selected : false;
|
||||
|
||||
if (is_point_clipped(support_point.pos.cast<double>()))
|
||||
if (is_mesh_point_clipped(support_point.pos.cast<double>()))
|
||||
continue;
|
||||
|
||||
// First decide about the color of the point.
|
||||
|
@ -335,14 +335,14 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
|
||||
|
||||
|
||||
bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point) const
|
||||
bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const
|
||||
{
|
||||
if (m_clipping_plane_distance == 0.f)
|
||||
return false;
|
||||
|
||||
Vec3d transformed_point = m_model_object->instances.front()->get_transformation().get_matrix() * point;
|
||||
transformed_point(2) += m_z_shift;
|
||||
return m_clipping_plane->distance(transformed_point) < 0.;
|
||||
return m_clipping_plane->is_point_clipped(transformed_point);
|
||||
}
|
||||
|
||||
|
||||
|
@ -391,27 +391,15 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec
|
|||
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift));
|
||||
|
||||
// The raycaster query
|
||||
std::vector<Vec3f> hits;
|
||||
std::vector<Vec3f> normals;
|
||||
m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, &hits, &normals);
|
||||
|
||||
// We must also take care of the clipping plane (if active)
|
||||
unsigned i = 0;
|
||||
if (m_clipping_plane_distance != 0.f) {
|
||||
for (i=0; i<hits.size(); ++i)
|
||||
if (! is_point_clipped(hits[i].cast<double>()))
|
||||
break;
|
||||
Vec3f hit;
|
||||
Vec3f normal;
|
||||
if (m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get())) {
|
||||
// Return both the point and the facet normal.
|
||||
pos_and_normal = std::make_pair(hit, normal);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (i==hits.size() || (hits.size()-i) % 2 != 0) {
|
||||
// All hits are either clipped, or there is an odd number of unclipped
|
||||
// hits - meaning the nearest must be from inside the mesh.
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Calculate and return both the point and the facet normal.
|
||||
pos_and_normal = std::make_pair(hits[i], normals[i]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event.
|
||||
|
@ -481,19 +469,15 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
std::vector<Vec3f> points_inside;
|
||||
std::vector<unsigned int> points_idxs = m_selection_rectangle.stop_dragging(m_parent, points);
|
||||
for (size_t idx : points_idxs)
|
||||
points_inside.push_back((trafo.get_matrix() * points[idx]).cast<float>());
|
||||
points_inside.push_back(points[idx].cast<float>());
|
||||
|
||||
// Only select/deselect points that are actually visible
|
||||
for (size_t idx : m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside,
|
||||
[this](const Vec3f& pt) { return is_point_clipped(pt.cast<double>()); }))
|
||||
for (size_t idx : m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get()))
|
||||
{
|
||||
const sla::SupportPoint &support_point = m_editing_cache[points_idxs[idx]].support_point;
|
||||
if (! is_point_clipped(support_point.pos.cast<double>())) {
|
||||
if (rectangle_status == GLSelectionRectangle::Deselect)
|
||||
unselect_point(points_idxs[idx]);
|
||||
else
|
||||
select_point(points_idxs[idx]);
|
||||
}
|
||||
if (rectangle_status == GLSelectionRectangle::Deselect)
|
||||
unselect_point(points_idxs[idx]);
|
||||
else
|
||||
select_point(points_idxs[idx]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@ private:
|
|||
mutable std::unique_ptr<MeshClipper> m_supports_clipper;
|
||||
|
||||
std::vector<const ConfigOption*> get_config_options(const std::vector<std::string>& keys) const;
|
||||
bool is_point_clipped(const Vec3d& point) const;
|
||||
bool is_mesh_point_clipped(const Vec3d& point) const;
|
||||
//void find_intersecting_facets(const igl::AABB<Eigen::MatrixXf, 3>* aabb, const Vec3f& normal, double offset, std::vector<unsigned int>& out) const;
|
||||
|
||||
// Methods that do the model_object and editing cache synchronization,
|
||||
|
|
|
@ -192,7 +192,7 @@ ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(const wx
|
|||
else
|
||||
panel->SetSizer(optgroup->sizer);
|
||||
|
||||
return optgroup;
|
||||
return optgroup;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -917,7 +917,7 @@ void MainFrame::load_config_file()
|
|||
wxString file;
|
||||
if (dlg.ShowModal() == wxID_OK)
|
||||
file = dlg.GetPath();
|
||||
if (! file.IsEmpty() && this->load_config_file(file.ToUTF8().data())) {
|
||||
if (! file.IsEmpty() && this->load_config_file(file.ToUTF8().data())) {
|
||||
wxGetApp().app_config->update_config_dir(get_dir_name(file));
|
||||
m_last_config = file;
|
||||
}
|
||||
|
|
|
@ -152,8 +152,8 @@ Vec3f MeshRaycaster::AABBWrapper::get_hit_normal(const igl::Hit& hit) const
|
|||
}
|
||||
|
||||
|
||||
bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo,
|
||||
const Camera& camera, std::vector<Vec3f>* positions, std::vector<Vec3f>* normals) const
|
||||
bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
|
||||
Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane) const
|
||||
{
|
||||
const std::array<int, 4>& viewport = camera.get_viewport();
|
||||
const Transform3d& model_mat = camera.get_view_matrix();
|
||||
|
@ -179,25 +179,30 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d&
|
|||
|
||||
std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; });
|
||||
|
||||
// Now stuff the points in the provided vector and calculate normals if asked about them:
|
||||
if (positions != nullptr) {
|
||||
positions->clear();
|
||||
if (normals != nullptr)
|
||||
normals->clear();
|
||||
for (const igl::Hit& hit : hits) {
|
||||
positions->push_back(m_AABB_wrapper->get_hit_pos(hit));
|
||||
unsigned i = 0;
|
||||
|
||||
if (normals != nullptr)
|
||||
normals->push_back(m_AABB_wrapper->get_hit_normal(hit));
|
||||
// Remove points that are obscured or cut by the clipping plane
|
||||
if (clipping_plane) {
|
||||
for (i=0; i<hits.size(); ++i)
|
||||
if (! clipping_plane->is_point_clipped(trafo * m_AABB_wrapper->get_hit_pos(hits[i]).cast<double>()))
|
||||
break;
|
||||
|
||||
if (i==hits.size() || (hits.size()-i) % 2 != 0) {
|
||||
// All hits are either clipped, or there is an odd number of unclipped
|
||||
// hits - meaning the nearest must be from inside the mesh.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Now stuff the points in the provided vector and calculate normals if asked about them:
|
||||
position = m_AABB_wrapper->get_hit_pos(hits[i]);
|
||||
normal = m_AABB_wrapper->get_hit_normal(hits[i]);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera, const std::vector<Vec3f>& points,
|
||||
std::function<bool(const Vec3f&)> fn_ignore_hit) const
|
||||
const ClippingPlane* clipping_plane) const
|
||||
{
|
||||
std::vector<unsigned> out;
|
||||
|
||||
|
@ -206,19 +211,24 @@ std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo
|
|||
Vec3f direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse().cast<float>() * direction_to_camera).normalized().eval();
|
||||
Vec3f scaling = trafo.get_scaling_factor().cast<float>();
|
||||
direction_to_camera_mesh = Vec3f(direction_to_camera_mesh(0)*scaling(0), direction_to_camera_mesh(1)*scaling(1), direction_to_camera_mesh(2)*scaling(2));
|
||||
const Transform3f inverse_trafo = trafo.get_matrix().inverse().cast<float>();
|
||||
|
||||
for (size_t i=0; i<points.size(); ++i) {
|
||||
const Vec3f& pt = points[i];
|
||||
if (clipping_plane && clipping_plane->is_point_clipped(pt.cast<double>()))
|
||||
continue;
|
||||
|
||||
bool is_obscured = false;
|
||||
// Cast a ray in the direction of the camera and look for intersection with the mesh:
|
||||
std::vector<igl::Hit> hits;
|
||||
// Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies.
|
||||
// Offset the start of the ray by EPSILON to account for numerical inaccuracies.
|
||||
if (m_AABB_wrapper->m_AABB.intersect_ray(
|
||||
AABBWrapper::MapMatrixXfUnaligned(m_mesh->its.vertices.front().data(), m_mesh->its.vertices.size(), 3),
|
||||
AABBWrapper::MapMatrixXiUnaligned(m_mesh->its.indices.front().data(), m_mesh->its.indices.size(), 3),
|
||||
pt + direction_to_camera_mesh * EPSILON, direction_to_camera_mesh, hits)) {
|
||||
inverse_trafo * pt + direction_to_camera_mesh * EPSILON, direction_to_camera_mesh, hits)) {
|
||||
|
||||
std::sort(hits.begin(), hits.end(), [](const igl::Hit& h1, const igl::Hit& h2) { return h1.t < h2.t; });
|
||||
|
||||
// If the closest hit facet normal points in the same direction as the ray,
|
||||
// we are looking through the mesh and should therefore discard the point:
|
||||
if (m_AABB_wrapper->get_hit_normal(hits.front()).dot(direction_to_camera_mesh) > 0.f)
|
||||
|
@ -227,11 +237,12 @@ std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo
|
|||
// Eradicate all hits that the caller wants to ignore
|
||||
for (unsigned j=0; j<hits.size(); ++j) {
|
||||
const igl::Hit& hit = hits[j];
|
||||
if (fn_ignore_hit(m_AABB_wrapper->get_hit_pos(hit))) {
|
||||
if (clipping_plane && clipping_plane->is_point_clipped(trafo.get_matrix() * m_AABB_wrapper->get_hit_pos(hit).cast<double>())) {
|
||||
hits.erase(hits.begin()+j);
|
||||
--j;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: the intersection could in theory be behind the camera, but as of now we only have camera direction.
|
||||
// Also, the threshold is in mesh coordinates, not in actual dimensions.
|
||||
if (! hits.empty())
|
||||
|
|
|
@ -50,6 +50,7 @@ public:
|
|||
return (-get_normal().dot(pt) + m_data[3]);
|
||||
}
|
||||
|
||||
bool is_point_clipped(const Vec3d& point) const { return distance(point) < 0.; }
|
||||
void set_normal(const Vec3d& normal) { for (size_t i=0; i<3; ++i) m_data[i] = normal(i); }
|
||||
void set_offset(double offset) { m_data[3] = offset; }
|
||||
Vec3d get_normal() const { return Vec3d(m_data[0], m_data[1], m_data[2]); }
|
||||
|
@ -98,10 +99,10 @@ public:
|
|||
void set_camera(const Camera& camera);
|
||||
|
||||
bool unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
|
||||
std::vector<Vec3f>* positions = nullptr, std::vector<Vec3f>* normals = nullptr) const;
|
||||
Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane = nullptr) const;
|
||||
|
||||
std::vector<unsigned> get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera,
|
||||
const std::vector<Vec3f>& points, std::function<bool(const Vec3f&)> fn_ignore_hit) const;
|
||||
const std::vector<Vec3f>& points, const ClippingPlane* clipping_plane = nullptr) const;
|
||||
|
||||
Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const;
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
|
|||
m_options_mode.push_back(option_set[0].opt.mode);
|
||||
|
||||
// if we have a single option with no label, no sidetext just add it directly to sizer
|
||||
if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width &&
|
||||
if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width &&
|
||||
option_set.front().opt.label.empty() &&
|
||||
option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr &&
|
||||
line.get_extra_widgets().size() == 0) {
|
||||
|
|
|
@ -3322,7 +3322,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
|
|||
this->statusbar()->set_progress(evt.status.percent);
|
||||
this->statusbar()->set_status_text(_(evt.status.text) + wxString::FromUTF8("…"));
|
||||
}
|
||||
if (evt.status.flags & (PrintBase::SlicingStatus::RELOAD_SCENE || PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS)) {
|
||||
if (evt.status.flags & (PrintBase::SlicingStatus::RELOAD_SCENE | PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS)) {
|
||||
switch (this->printer_technology) {
|
||||
case ptFFF:
|
||||
this->update_fff_scene();
|
||||
|
@ -4288,11 +4288,10 @@ void Plater::increase_instances(size_t num)
|
|||
|
||||
sidebar().obj_list()->increase_object_instances(obj_idx, was_one_instance ? num + 1 : num);
|
||||
|
||||
if (p->get_config("autocenter") == "1") {
|
||||
if (p->get_config("autocenter") == "1")
|
||||
p->arrange();
|
||||
} else {
|
||||
p->update();
|
||||
}
|
||||
|
||||
p->update();
|
||||
|
||||
p->get_selection().add_instance(obj_idx, (int)model_object->instances.size() - 1);
|
||||
|
||||
|
|
|
@ -279,7 +279,7 @@ std::string PresetBundle::load_system_presets()
|
|||
errors_cummulative += "\n";
|
||||
}
|
||||
}
|
||||
if (first) {
|
||||
if (first) {
|
||||
// No config bundle loaded, reset.
|
||||
this->reset(false);
|
||||
}
|
||||
|
|
|
@ -410,7 +410,7 @@ void Selection::set_deserialized(EMode mode, const std::vector<std::pair<size_t,
|
|||
if (! m_valid)
|
||||
return;
|
||||
|
||||
m_mode = mode;
|
||||
m_mode = mode;
|
||||
for (unsigned int i : m_list)
|
||||
(*m_volumes)[i]->selected = false;
|
||||
m_list.clear();
|
||||
|
|
|
@ -371,7 +371,7 @@ void WipingPanel::toggle_advanced(bool user_action) {
|
|||
else
|
||||
m_advanced = !advanced_matches_simple(); // if called from constructor, show what is appropriate
|
||||
|
||||
(m_advanced ? m_page_advanced : m_page_simple)->Show();
|
||||
(m_advanced ? m_page_advanced : m_page_simple)->Show();
|
||||
(!m_advanced ? m_page_advanced : m_page_simple)->Hide();
|
||||
|
||||
m_widget_button->SetLabel(m_advanced ? _(L("Show simplified settings")) : _(L("Show advanced settings")));
|
||||
|
|
|
@ -669,7 +669,7 @@ wxDataViewItem ObjectDataViewModel::Add(const wxString &name,
|
|||
if (has_errors)
|
||||
root->m_bmp = *m_warning_bmp;
|
||||
|
||||
m_objects.push_back(root);
|
||||
m_objects.push_back(root);
|
||||
// notify control
|
||||
wxDataViewItem child((void*)root);
|
||||
wxDataViewItem parent((void*)NULL);
|
||||
|
@ -720,7 +720,7 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent
|
|||
root->SetBitmap(*m_warning_bmp);
|
||||
|
||||
// notify control
|
||||
const wxDataViewItem child((void*)node);
|
||||
const wxDataViewItem child((void*)node);
|
||||
ItemAdded(parent_item, child);
|
||||
root->m_volumes_cnt++;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue