Part skip from Orca for Bambu printers (#10875)

Ported part skip from BambuStudio. Thanks bambulab!

<img width="1350" height="902" alt="image"
src="https://github.com/user-attachments/assets/d7f89950-3420-4ea5-bbb6-e53c875e7422"
/>

Haven't fully tested yet!


### Minimum Firmware Requirements (from BambuStudio release page):

- **X1C**: v01.09.02.12 or later
- **H2D**: v01.02.00.00 or later
- **Other models**: Currently not supported. Feature support coming in
future firmware updates.

> ⚠️ Make sure your printer firmware is up to date to use this feature.
This commit is contained in:
SoftFever
2025-10-23 23:46:39 +08:00
committed by GitHub
39 changed files with 4447 additions and 168 deletions

3
.gitignore vendored
View File

@@ -39,3 +39,6 @@ src/OrcaSlicer-doc/
resources/profiles/user/default
*.code-workspace
deps_src/build/
test.js
/.cache/
.clangd

View File

@@ -53,6 +53,7 @@ orcaslicer_add_cmake_project(OpenCV
-DWITH_OPENJPEG=OFF
-DWITH_QUIRC=OFF
-DWITH_VTK=OFF
-DWITH_JPEG=OFF
-DWITH_WEBP=OFF
-DENABLE_PRECOMPILED_HEADERS=OFF
-DINSTALL_TESTS=OFF

View File

@@ -6,6 +6,7 @@ project(deps_src)
# Header-only libraries (INTERFACE)
add_subdirectory(agg)
add_subdirectory(ankerl)
add_subdirectory(earcut)
add_subdirectory(fast_float)
add_subdirectory(nanosvg)
add_subdirectory(nlohmann)

View File

@@ -0,0 +1,27 @@
## Earcut.hpp changelog
### master
- Fixed a bunch of rare edge cases that led to bad triangulation (parity with Earcut v2.2.2)
- Removed use of deprecated `std::allocator::construct`
- Fixed a minor z-order hashing bug
- Improved visualization app, better docs
### v0.12.4
- Fixed a crash in Crash in Earcut::findHoleBridge
- Added coverage checks
- Added macOS, MinGW builds
### v0.12.3
- Fixed -Wunused-lambda-capture
### v0.12.2
- Fixed potential division by zero
- Fixed -fsanitize=integer warning
### v0.12.1
- Fixed cast precision warning

View File

@@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.13)
project(earcut)
add_library(earcut INTERFACE)
target_include_directories(earcut SYSTEM
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}
)
target_sources(earcut INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/earcut.hpp
)

15
deps_src/earcut/LICENSE Normal file
View File

@@ -0,0 +1,15 @@
ISC License
Copyright (c) 2015, Mapbox
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.

131
deps_src/earcut/README.md Normal file
View File

@@ -0,0 +1,131 @@
## Earcut
A C++ port of [earcut.js](https://github.com/mapbox/earcut), a fast, [header-only](https://github.com/mapbox/earcut.hpp/blob/master/include/mapbox/earcut.hpp) polygon triangulation library.
[![Travis](https://img.shields.io/travis/com/mapbox/earcut.hpp.svg)](https://travis-ci.com/github/mapbox/earcut.hpp)
[![AppVeyor](https://ci.appveyor.com/api/projects/status/a1ysrqd69mqn7coo/branch/master?svg=true)](https://ci.appveyor.com/project/Mapbox/earcut-hpp-8wm4o/branch/master)
[![Coverage](https://img.shields.io/coveralls/github/mapbox/earcut.hpp.svg)](https://coveralls.io/github/mapbox/earcut.hpp)
[![Coverity Scan](https://img.shields.io/coverity/scan/14000.svg)](https://scan.coverity.com/projects/14000)
[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/mapbox/earcut.hpp.svg)](http://isitmaintained.com/project/mapbox/earcut.hpp "Average time to resolve an issue")
[![Percentage of issues still open](http://isitmaintained.com/badge/open/mapbox/earcut.hpp.svg)](http://isitmaintained.com/project/mapbox/earcut.hpp "Percentage of issues still open")
[![Mourner](https://img.shields.io/badge/simply-awesome-brightgreen.svg)](https://github.com/mourner/projects)
The library implements a modified ear slicing algorithm, optimized by [z-order curve](http://en.wikipedia.org/wiki/Z-order_curve) hashing and extended to handle holes, twisted polygons, degeneracies and self-intersections in a way that doesn't _guarantee_ correctness of triangulation, but attempts to always produce acceptable results for practical data like geographical shapes.
It's based on ideas from [FIST: Fast Industrial-Strength Triangulation of Polygons](http://www.cosy.sbg.ac.at/~held/projects/triang/triang.html) by Martin Held and [Triangulation by Ear Clipping](http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf) by David Eberly.
## Usage
```cpp
#include <earcut.hpp>
```
```cpp
// The number type to use for tessellation
using Coord = double;
// The index type. Defaults to uint32_t, but you can also pass uint16_t if you know that your
// data won't have more than 65536 vertices.
using N = uint32_t;
// Create array
using Point = std::array<Coord, 2>;
std::vector<std::vector<Point>> polygon;
// Fill polygon structure with actual data. Any winding order works.
// The first polyline defines the main polygon.
polygon.push_back({{100, 0}, {100, 100}, {0, 100}, {0, 0}});
// Following polylines define holes.
polygon.push_back({{75, 25}, {75, 75}, {25, 75}, {25, 25}});
// Run tessellation
// Returns array of indices that refer to the vertices of the input polygon.
// e.g: the index 6 would refer to {25, 75} in this example.
// Three subsequent indices form a triangle. Output triangles are clockwise.
std::vector<N> indices = mapbox::earcut<N>(polygon);
```
Earcut can triangulate a simple, planar polygon of any winding order including holes. It will even return a robust, acceptable solution for non-simple poygons. Earcut works on a 2D plane. If you have three or more dimensions, you can project them onto a 2D surface before triangulation, or use a more suitable library for the task (e.g [CGAL](https://doc.cgal.org/latest/Triangulation_3/index.html)).
It is also possible to use your custom point type as input. There are default accessors defined for `std::tuple`, `std::pair`, and `std::array`. For a custom type (like Clipper's `IntPoint` type), do this:
```cpp
// struct IntPoint {
// int64_t X, Y;
// };
namespace mapbox {
namespace util {
template <>
struct nth<0, IntPoint> {
inline static auto get(const IntPoint &t) {
return t.X;
};
};
template <>
struct nth<1, IntPoint> {
inline static auto get(const IntPoint &t) {
return t.Y;
};
};
} // namespace util
} // namespace mapbox
```
You can also use a custom container type for your polygon. Similar to std::vector<T>, it has to meet the requirements of [Container](https://en.cppreference.com/w/cpp/named_req/Container), in particular `size()`, `empty()` and `operator[]`.
<p align="center">
<img src="https://camo.githubusercontent.com/01836f8ba21af844c93d8d3145f4e9976025a696/68747470733a2f2f692e696d6775722e636f6d2f67314e704c54712e706e67" alt="example triangulation"/>
</p>
## Additional build instructions
In case you just want to use the earcut triangulation library; copy and include the header file [`<earcut.hpp>`](https://github.com/mapbox/earcut.hpp/blob/master/include/mapbox/earcut.hpp) in your project and follow the steps documented in the section [Usage](#usage).
If you want to build the test, benchmark and visualization programs instead, follow these instructions:
### Dependencies
Before you continue, make sure to have the following tools and libraries installed:
* git ([Ubuntu](https://help.ubuntu.com/lts/serverguide/git.html)/[Windows/macOS](http://git-scm.com/downloads))
* cmake 3.2+ ([Ubuntu](https://launchpad.net/~george-edison55/+archive/ubuntu/cmake-3.x)/[Windows/macOS](https://cmake.org/download/))
* OpenGL SDK ([Ubuntu](http://packages.ubuntu.com/de/trusty/libgl1-mesa-dev)/[Windows](https://dev.windows.com/en-us/downloads/windows-10-sdk)/[macOS](https://developer.apple.com/opengl/))
* Compiler such as [GCC 4.9+, Clang 3.4+](https://launchpad.net/~ubuntu-toolchain-r/+archive/ubuntu/test), [MSVC12+](https://www.visualstudio.com/)
Note: On some operating systems such as Windows, manual steps are required to add cmake and [git](http://blog.countableset.ch/2012/06/07/adding-git-to-windows-7-path/) to your PATH environment variable.
### Manual compilation
```bash
git clone --recursive https://github.com/mapbox/earcut.hpp.git
cd earcut.hpp
mkdir build
cd build
cmake ..
make
# ./tests
# ./bench
# ./viz
```
### [Visual Studio](https://www.visualstudio.com/), [Eclipse](https://eclipse.org/), [XCode](https://developer.apple.com/xcode/), ...
```batch
git clone --recursive https://github.com/mapbox/earcut.hpp.git
cd earcut.hpp
mkdir project
cd project
cmake .. -G "Visual Studio 14 2015"
::you can also generate projects for "Visual Studio 12 2013", "XCode", "Eclipse CDT4 - Unix Makefiles"
```
After completion, open the generated project with your IDE.
### [CLion](https://www.jetbrains.com/clion/), [Visual Studio 2017+](https://www.visualstudio.com/)
Import the project from https://github.com/mapbox/earcut.hpp.git and you should be good to go!
## Status
This is currently based on [earcut 2.2.4](https://github.com/mapbox/earcut#224-jul-5-2022).

814
deps_src/earcut/earcut.hpp Normal file
View File

@@ -0,0 +1,814 @@
#pragma once
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <memory>
#include <utility>
#include <vector>
namespace mapbox {
namespace util {
template <std::size_t I, typename T> struct nth {
inline static typename std::tuple_element<I, T>::type
get(const T& t) { return std::get<I>(t); };
};
}
namespace detail {
template <typename N = uint32_t>
class Earcut {
public:
std::vector<N> indices;
std::size_t vertices = 0;
template <typename Polygon>
void operator()(const Polygon& points);
private:
struct Node {
Node(N index, double x_, double y_) : i(index), x(x_), y(y_) {}
Node(const Node&) = delete;
Node& operator=(const Node&) = delete;
Node(Node&&) = delete;
Node& operator=(Node&&) = delete;
const N i;
const double x;
const double y;
// previous and next vertice nodes in a polygon ring
Node* prev = nullptr;
Node* next = nullptr;
// z-order curve value
int32_t z = 0;
// previous and next nodes in z-order
Node* prevZ = nullptr;
Node* nextZ = nullptr;
// indicates whether this is a steiner point
bool steiner = false;
};
template <typename Ring> Node* linkedList(const Ring& points, const bool clockwise);
Node* filterPoints(Node* start, Node* end = nullptr);
void earcutLinked(Node* ear, int pass = 0);
bool isEar(Node* ear);
bool isEarHashed(Node* ear);
Node* cureLocalIntersections(Node* start);
void splitEarcut(Node* start);
template <typename Polygon> Node* eliminateHoles(const Polygon& points, Node* outerNode);
Node* eliminateHole(Node* hole, Node* outerNode);
Node* findHoleBridge(Node* hole, Node* outerNode);
bool sectorContainsSector(const Node* m, const Node* p);
void indexCurve(Node* start);
Node* sortLinked(Node* list);
int32_t zOrder(const double x_, const double y_);
Node* getLeftmost(Node* start);
bool pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const;
bool isValidDiagonal(Node* a, Node* b);
double area(const Node* p, const Node* q, const Node* r) const;
bool equals(const Node* p1, const Node* p2);
bool intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2);
bool onSegment(const Node* p, const Node* q, const Node* r);
int sign(double val);
bool intersectsPolygon(const Node* a, const Node* b);
bool locallyInside(const Node* a, const Node* b);
bool middleInside(const Node* a, const Node* b);
Node* splitPolygon(Node* a, Node* b);
template <typename Point> Node* insertNode(std::size_t i, const Point& p, Node* last);
void removeNode(Node* p);
bool hashing;
double minX, maxX;
double minY, maxY;
double inv_size = 0;
template <typename T, typename Alloc = std::allocator<T>>
class ObjectPool {
public:
ObjectPool() { }
ObjectPool(std::size_t blockSize_) {
reset(blockSize_);
}
~ObjectPool() {
clear();
}
template <typename... Args>
T* construct(Args&&... args) {
if (currentIndex >= blockSize) {
currentBlock = alloc_traits::allocate(alloc, blockSize);
allocations.emplace_back(currentBlock);
currentIndex = 0;
}
T* object = &currentBlock[currentIndex++];
alloc_traits::construct(alloc, object, std::forward<Args>(args)...);
return object;
}
void reset(std::size_t newBlockSize) {
for (auto allocation : allocations) {
alloc_traits::deallocate(alloc, allocation, blockSize);
}
allocations.clear();
blockSize = std::max<std::size_t>(1, newBlockSize);
currentBlock = nullptr;
currentIndex = blockSize;
}
void clear() { reset(blockSize); }
private:
T* currentBlock = nullptr;
std::size_t currentIndex = 1;
std::size_t blockSize = 1;
std::vector<T*> allocations;
Alloc alloc;
typedef typename std::allocator_traits<Alloc> alloc_traits;
};
ObjectPool<Node> nodes;
};
template <typename N> template <typename Polygon>
void Earcut<N>::operator()(const Polygon& points) {
// reset
indices.clear();
vertices = 0;
if (points.empty()) return;
double x;
double y;
int threshold = 80;
std::size_t len = 0;
for (size_t i = 0; threshold >= 0 && i < points.size(); i++) {
threshold -= static_cast<int>(points[i].size());
len += points[i].size();
}
//estimate size of nodes and indices
nodes.reset(len * 3 / 2);
indices.reserve(len + points[0].size());
Node* outerNode = linkedList(points[0], true);
if (!outerNode || outerNode->prev == outerNode->next) return;
if (points.size() > 1) outerNode = eliminateHoles(points, outerNode);
// if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
hashing = threshold < 0;
if (hashing) {
Node* p = outerNode->next;
minX = maxX = outerNode->x;
minY = maxY = outerNode->y;
do {
x = p->x;
y = p->y;
minX = std::min<double>(minX, x);
minY = std::min<double>(minY, y);
maxX = std::max<double>(maxX, x);
maxY = std::max<double>(maxY, y);
p = p->next;
} while (p != outerNode);
// minX, minY and inv_size are later used to transform coords into integers for z-order calculation
inv_size = std::max<double>(maxX - minX, maxY - minY);
inv_size = inv_size != .0 ? (32767. / inv_size) : .0;
}
earcutLinked(outerNode);
nodes.clear();
}
// create a circular doubly linked list from polygon points in the specified winding order
template <typename N> template <typename Ring>
typename Earcut<N>::Node*
Earcut<N>::linkedList(const Ring& points, const bool clockwise) {
using Point = typename Ring::value_type;
double sum = 0;
const std::size_t len = points.size();
std::size_t i, j;
Node* last = nullptr;
// calculate original winding order of a polygon ring
for (i = 0, j = len > 0 ? len - 1 : 0; i < len; j = i++) {
const auto& p1 = points[i];
const auto& p2 = points[j];
const double p20 = util::nth<0, Point>::get(p2);
const double p10 = util::nth<0, Point>::get(p1);
const double p11 = util::nth<1, Point>::get(p1);
const double p21 = util::nth<1, Point>::get(p2);
sum += (p20 - p10) * (p11 + p21);
}
// link points into circular doubly-linked list in the specified winding order
if (clockwise == (sum > 0)) {
for (i = 0; i < len; i++) last = insertNode(vertices + i, points[i], last);
} else {
for (i = len; i-- > 0;) last = insertNode(vertices + i, points[i], last);
}
if (last && equals(last, last->next)) {
removeNode(last);
last = last->next;
}
vertices += len;
return last;
}
// eliminate colinear or duplicate points
template <typename N>
typename Earcut<N>::Node*
Earcut<N>::filterPoints(Node* start, Node* end) {
if (!end) end = start;
Node* p = start;
bool again;
do {
again = false;
if (!p->steiner && (equals(p, p->next) || area(p->prev, p, p->next) == 0)) {
removeNode(p);
p = end = p->prev;
if (p == p->next) break;
again = true;
} else {
p = p->next;
}
} while (again || p != end);
return end;
}
// main ear slicing loop which triangulates a polygon (given as a linked list)
template <typename N>
void Earcut<N>::earcutLinked(Node* ear, int pass) {
if (!ear) return;
// interlink polygon nodes in z-order
if (!pass && hashing) indexCurve(ear);
Node* stop = ear;
Node* prev;
Node* next;
// iterate through ears, slicing them one by one
while (ear->prev != ear->next) {
prev = ear->prev;
next = ear->next;
if (hashing ? isEarHashed(ear) : isEar(ear)) {
// cut off the triangle
indices.emplace_back(prev->i);
indices.emplace_back(ear->i);
indices.emplace_back(next->i);
removeNode(ear);
// skipping the next vertice leads to less sliver triangles
ear = next->next;
stop = next->next;
continue;
}
ear = next;
// if we looped through the whole remaining polygon and can't find any more ears
if (ear == stop) {
// try filtering points and slicing again
if (!pass) earcutLinked(filterPoints(ear), 1);
// if this didn't work, try curing all small self-intersections locally
else if (pass == 1) {
ear = cureLocalIntersections(filterPoints(ear));
earcutLinked(ear, 2);
// as a last resort, try splitting the remaining polygon into two
} else if (pass == 2) splitEarcut(ear);
break;
}
}
}
// check whether a polygon node forms a valid ear with adjacent nodes
template <typename N>
bool Earcut<N>::isEar(Node* ear) {
const Node* a = ear->prev;
const Node* b = ear;
const Node* c = ear->next;
if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
// now make sure we don't have other points inside the potential ear
Node* p = ear->next->next;
while (p != ear->prev) {
if (pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
area(p->prev, p, p->next) >= 0) return false;
p = p->next;
}
return true;
}
template <typename N>
bool Earcut<N>::isEarHashed(Node* ear) {
const Node* a = ear->prev;
const Node* b = ear;
const Node* c = ear->next;
if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
// triangle bbox; min & max are calculated like this for speed
const double minTX = std::min<double>(a->x, std::min<double>(b->x, c->x));
const double minTY = std::min<double>(a->y, std::min<double>(b->y, c->y));
const double maxTX = std::max<double>(a->x, std::max<double>(b->x, c->x));
const double maxTY = std::max<double>(a->y, std::max<double>(b->y, c->y));
// z-order range for the current triangle bbox;
const int32_t minZ = zOrder(minTX, minTY);
const int32_t maxZ = zOrder(maxTX, maxTY);
// first look for points inside the triangle in increasing z-order
Node* p = ear->nextZ;
while (p && p->z <= maxZ) {
if (p != ear->prev && p != ear->next &&
pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
area(p->prev, p, p->next) >= 0) return false;
p = p->nextZ;
}
// then look for points in decreasing z-order
p = ear->prevZ;
while (p && p->z >= minZ) {
if (p != ear->prev && p != ear->next &&
pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
area(p->prev, p, p->next) >= 0) return false;
p = p->prevZ;
}
return true;
}
// go through all polygon nodes and cure small local self-intersections
template <typename N>
typename Earcut<N>::Node*
Earcut<N>::cureLocalIntersections(Node* start) {
Node* p = start;
do {
Node* a = p->prev;
Node* b = p->next->next;
// a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2])
if (!equals(a, b) && intersects(a, p, p->next, b) && locallyInside(a, b) && locallyInside(b, a)) {
indices.emplace_back(a->i);
indices.emplace_back(p->i);
indices.emplace_back(b->i);
// remove two nodes involved
removeNode(p);
removeNode(p->next);
p = start = b;
}
p = p->next;
} while (p != start);
return filterPoints(p);
}
// try splitting polygon into two and triangulate them independently
template <typename N>
void Earcut<N>::splitEarcut(Node* start) {
// look for a valid diagonal that divides the polygon into two
Node* a = start;
do {
Node* b = a->next->next;
while (b != a->prev) {
if (a->i != b->i && isValidDiagonal(a, b)) {
// split the polygon in two by the diagonal
Node* c = splitPolygon(a, b);
// filter colinear points around the cuts
a = filterPoints(a, a->next);
c = filterPoints(c, c->next);
// run earcut on each half
earcutLinked(a);
earcutLinked(c);
return;
}
b = b->next;
}
a = a->next;
} while (a != start);
}
// link every hole into the outer loop, producing a single-ring polygon without holes
template <typename N> template <typename Polygon>
typename Earcut<N>::Node*
Earcut<N>::eliminateHoles(const Polygon& points, Node* outerNode) {
const size_t len = points.size();
std::vector<Node*> queue;
for (size_t i = 1; i < len; i++) {
Node* list = linkedList(points[i], false);
if (list) {
if (list == list->next) list->steiner = true;
queue.push_back(getLeftmost(list));
}
}
std::sort(queue.begin(), queue.end(), [](const Node* a, const Node* b) {
return a->x < b->x;
});
// process holes from left to right
for (size_t i = 0; i < queue.size(); i++) {
outerNode = eliminateHole(queue[i], outerNode);
}
return outerNode;
}
// find a bridge between vertices that connects hole with an outer ring and and link it
template <typename N>
typename Earcut<N>::Node*
Earcut<N>::eliminateHole(Node* hole, Node* outerNode) {
Node* bridge = findHoleBridge(hole, outerNode);
if (!bridge) {
return outerNode;
}
Node* bridgeReverse = splitPolygon(bridge, hole);
// filter collinear points around the cuts
filterPoints(bridgeReverse, bridgeReverse->next);
// Check if input node was removed by the filtering
return filterPoints(bridge, bridge->next);
}
// David Eberly's algorithm for finding a bridge between hole and outer polygon
template <typename N>
typename Earcut<N>::Node*
Earcut<N>::findHoleBridge(Node* hole, Node* outerNode) {
Node* p = outerNode;
double hx = hole->x;
double hy = hole->y;
double qx = -std::numeric_limits<double>::infinity();
Node* m = nullptr;
// find a segment intersected by a ray from the hole's leftmost Vertex to the left;
// segment's endpoint with lesser x will be potential connection Vertex
do {
if (hy <= p->y && hy >= p->next->y && p->next->y != p->y) {
double x = p->x + (hy - p->y) * (p->next->x - p->x) / (p->next->y - p->y);
if (x <= hx && x > qx) {
qx = x;
m = p->x < p->next->x ? p : p->next;
if (x == hx) return m; // hole touches outer segment; pick leftmost endpoint
}
}
p = p->next;
} while (p != outerNode);
if (!m) return 0;
// look for points inside the triangle of hole Vertex, segment intersection and endpoint;
// if there are no points found, we have a valid connection;
// otherwise choose the Vertex of the minimum angle with the ray as connection Vertex
const Node* stop = m;
double tanMin = std::numeric_limits<double>::infinity();
double tanCur = 0;
p = m;
double mx = m->x;
double my = m->y;
do {
if (hx >= p->x && p->x >= mx && hx != p->x &&
pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p->x, p->y)) {
tanCur = std::abs(hy - p->y) / (hx - p->x); // tangential
if (locallyInside(p, hole) &&
(tanCur < tanMin || (tanCur == tanMin && (p->x > m->x || sectorContainsSector(m, p))))) {
m = p;
tanMin = tanCur;
}
}
p = p->next;
} while (p != stop);
return m;
}
// whether sector in vertex m contains sector in vertex p in the same coordinates
template <typename N>
bool Earcut<N>::sectorContainsSector(const Node* m, const Node* p) {
return area(m->prev, m, p->prev) < 0 && area(p->next, m, m->next) < 0;
}
// interlink polygon nodes in z-order
template <typename N>
void Earcut<N>::indexCurve(Node* start) {
assert(start);
Node* p = start;
do {
p->z = p->z ? p->z : zOrder(p->x, p->y);
p->prevZ = p->prev;
p->nextZ = p->next;
p = p->next;
} while (p != start);
p->prevZ->nextZ = nullptr;
p->prevZ = nullptr;
sortLinked(p);
}
// Simon Tatham's linked list merge sort algorithm
// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
template <typename N>
typename Earcut<N>::Node*
Earcut<N>::sortLinked(Node* list) {
assert(list);
Node* p;
Node* q;
Node* e;
Node* tail;
int i, numMerges, pSize, qSize;
int inSize = 1;
for (;;) {
p = list;
list = nullptr;
tail = nullptr;
numMerges = 0;
while (p) {
numMerges++;
q = p;
pSize = 0;
for (i = 0; i < inSize; i++) {
pSize++;
q = q->nextZ;
if (!q) break;
}
qSize = inSize;
while (pSize > 0 || (qSize > 0 && q)) {
if (pSize == 0) {
e = q;
q = q->nextZ;
qSize--;
} else if (qSize == 0 || !q) {
e = p;
p = p->nextZ;
pSize--;
} else if (p->z <= q->z) {
e = p;
p = p->nextZ;
pSize--;
} else {
e = q;
q = q->nextZ;
qSize--;
}
if (tail) tail->nextZ = e;
else list = e;
e->prevZ = tail;
tail = e;
}
p = q;
}
tail->nextZ = nullptr;
if (numMerges <= 1) return list;
inSize *= 2;
}
}
// z-order of a Vertex given coords and size of the data bounding box
template <typename N>
int32_t Earcut<N>::zOrder(const double x_, const double y_) {
// coords are transformed into non-negative 15-bit integer range
int32_t x = static_cast<int32_t>((x_ - minX) * inv_size);
int32_t y = static_cast<int32_t>((y_ - minY) * inv_size);
x = (x | (x << 8)) & 0x00FF00FF;
x = (x | (x << 4)) & 0x0F0F0F0F;
x = (x | (x << 2)) & 0x33333333;
x = (x | (x << 1)) & 0x55555555;
y = (y | (y << 8)) & 0x00FF00FF;
y = (y | (y << 4)) & 0x0F0F0F0F;
y = (y | (y << 2)) & 0x33333333;
y = (y | (y << 1)) & 0x55555555;
return x | (y << 1);
}
// find the leftmost node of a polygon ring
template <typename N>
typename Earcut<N>::Node*
Earcut<N>::getLeftmost(Node* start) {
Node* p = start;
Node* leftmost = start;
do {
if (p->x < leftmost->x || (p->x == leftmost->x && p->y < leftmost->y))
leftmost = p;
p = p->next;
} while (p != start);
return leftmost;
}
// check if a point lies within a convex triangle
template <typename N>
bool Earcut<N>::pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const {
return (cx - px) * (ay - py) >= (ax - px) * (cy - py) &&
(ax - px) * (by - py) >= (bx - px) * (ay - py) &&
(bx - px) * (cy - py) >= (cx - px) * (by - py);
}
// check if a diagonal between two polygon nodes is valid (lies in polygon interior)
template <typename N>
bool Earcut<N>::isValidDiagonal(Node* a, Node* b) {
return a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon(a, b) && // dones't intersect other edges
((locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
(area(a->prev, a, b->prev) != 0.0 || area(a, b->prev, b) != 0.0)) || // does not create opposite-facing sectors
(equals(a, b) && area(a->prev, a, a->next) > 0 && area(b->prev, b, b->next) > 0)); // special zero-length case
}
// signed area of a triangle
template <typename N>
double Earcut<N>::area(const Node* p, const Node* q, const Node* r) const {
return (q->y - p->y) * (r->x - q->x) - (q->x - p->x) * (r->y - q->y);
}
// check if two points are equal
template <typename N>
bool Earcut<N>::equals(const Node* p1, const Node* p2) {
return p1->x == p2->x && p1->y == p2->y;
}
// check if two segments intersect
template <typename N>
bool Earcut<N>::intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2) {
int o1 = sign(area(p1, q1, p2));
int o2 = sign(area(p1, q1, q2));
int o3 = sign(area(p2, q2, p1));
int o4 = sign(area(p2, q2, q1));
if (o1 != o2 && o3 != o4) return true; // general case
if (o1 == 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1
if (o2 == 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1
if (o3 == 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2
if (o4 == 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2
return false;
}
// for collinear points p, q, r, check if point q lies on segment pr
template <typename N>
bool Earcut<N>::onSegment(const Node* p, const Node* q, const Node* r) {
return q->x <= std::max<double>(p->x, r->x) &&
q->x >= std::min<double>(p->x, r->x) &&
q->y <= std::max<double>(p->y, r->y) &&
q->y >= std::min<double>(p->y, r->y);
}
template <typename N>
int Earcut<N>::sign(double val) {
return (0.0 < val) - (val < 0.0);
}
// check if a polygon diagonal intersects any polygon segments
template <typename N>
bool Earcut<N>::intersectsPolygon(const Node* a, const Node* b) {
const Node* p = a;
do {
if (p->i != a->i && p->next->i != a->i && p->i != b->i && p->next->i != b->i &&
intersects(p, p->next, a, b)) return true;
p = p->next;
} while (p != a);
return false;
}
// check if a polygon diagonal is locally inside the polygon
template <typename N>
bool Earcut<N>::locallyInside(const Node* a, const Node* b) {
return area(a->prev, a, a->next) < 0 ?
area(a, b, a->next) >= 0 && area(a, a->prev, b) >= 0 :
area(a, b, a->prev) < 0 || area(a, a->next, b) < 0;
}
// check if the middle Vertex of a polygon diagonal is inside the polygon
template <typename N>
bool Earcut<N>::middleInside(const Node* a, const Node* b) {
const Node* p = a;
bool inside = false;
double px = (a->x + b->x) / 2;
double py = (a->y + b->y) / 2;
do {
if (((p->y > py) != (p->next->y > py)) && p->next->y != p->y &&
(px < (p->next->x - p->x) * (py - p->y) / (p->next->y - p->y) + p->x))
inside = !inside;
p = p->next;
} while (p != a);
return inside;
}
// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits
// polygon into two; if one belongs to the outer ring and another to a hole, it merges it into a
// single ring
template <typename N>
typename Earcut<N>::Node*
Earcut<N>::splitPolygon(Node* a, Node* b) {
Node* a2 = nodes.construct(a->i, a->x, a->y);
Node* b2 = nodes.construct(b->i, b->x, b->y);
Node* an = a->next;
Node* bp = b->prev;
a->next = b;
b->prev = a;
a2->next = an;
an->prev = a2;
b2->next = a2;
a2->prev = b2;
bp->next = b2;
b2->prev = bp;
return b2;
}
// create a node and util::optionally link it with previous one (in a circular doubly linked list)
template <typename N> template <typename Point>
typename Earcut<N>::Node*
Earcut<N>::insertNode(std::size_t i, const Point& pt, Node* last) {
Node* p = nodes.construct(static_cast<N>(i), util::nth<0, Point>::get(pt), util::nth<1, Point>::get(pt));
if (!last) {
p->prev = p;
p->next = p;
} else {
assert(last);
p->next = last->next;
p->prev = last;
last->next->prev = p;
last->next = p;
}
return p;
}
template <typename N>
void Earcut<N>::removeNode(Node* p) {
p->next->prev = p->prev;
p->prev->next = p->next;
if (p->prevZ) p->prevZ->nextZ = p->nextZ;
if (p->nextZ) p->nextZ->prevZ = p->prevZ;
}
}
template <typename N = uint32_t, typename Polygon>
std::vector<N> earcut(const Polygon& poly) {
mapbox::detail::Earcut<N> earcut;
earcut(poly);
return std::move(earcut.indices);
}
}

View File

@@ -177,3 +177,7 @@ src/slic3r/Utils/Flashforge.cpp
src/slic3r/GUI/Jobs/OAuthJob.cpp
src/slic3r/GUI/BackgroundSlicingProcess.cpp
src/slic3r/GUI/Gizmos/GLGizmoBrimEars.cpp
src/slic3r/GUI/PartSkipDialog.cpp
src/slic3r/GUI/PartSkipDialog.hpp
src/slic3r/GUI/SkipPartCanvas.cpp
src/slic3r/GUI/SkipPartCanvas.hpp

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.99838 1.66675C9.17481 1.66675 8.46192 2.14526 8.12338 2.83862C7.89551 2.75399 7.65137 2.70841 7.39421 2.70841C6.24512 2.70841 5.31088 3.64266 5.31088 4.79175V10.1791L5.22299 10.0912C4.40918 9.27742 3.09083 9.27742 2.27702 10.0912C1.46322 10.905 1.46322 12.2234 2.27702 13.0372L5.13184 15.892C6.69434 17.4545 8.81348 18.3334 11.0238 18.3334H11.3005H11.5609C11.6097 18.3334 11.6585 18.3302 11.7074 18.3204C14.6924 18.1186 17.0785 15.7358 17.277 12.7507C17.2868 12.7019 17.29 12.6531 17.29 12.6042V6.87508C17.29 5.72599 16.3558 4.79175 15.2067 4.79175C15.0277 4.79175 14.8519 4.81453 14.6859 4.85685V4.79175C14.6859 3.64266 13.7516 2.70841 12.6025 2.70841C12.3454 2.70841 12.1012 2.75399 11.8734 2.83862C11.5348 2.14526 10.8219 1.66675 9.99838 1.66675ZM9.47754 4.795V4.79175V3.75008C9.47754 3.46362 9.71192 3.22925 9.99838 3.22925C10.2848 3.22925 10.5192 3.46362 10.5192 3.75008V4.78849V4.79175V9.21883C10.5192 9.65177 10.8675 10.0001 11.3005 10.0001C11.7334 10.0001 12.0817 9.65177 12.0817 9.21883V4.79175C12.0817 4.79175 12.0817 4.79175 12.0817 4.78849C12.0817 4.50203 12.3161 4.26766 12.6025 4.26766C12.889 4.26766 13.1234 4.50203 13.1234 4.78849V6.60815V6.61141V9.21558C13.1234 9.64852 13.4717 9.99683 13.9046 9.99683C14.3376 9.99683 14.6859 9.64852 14.6859 9.21558V6.87834V6.87508C14.6859 6.58862 14.9203 6.35425 15.2067 6.35425C15.4932 6.35425 15.7275 6.58862 15.7275 6.87508V12.5033C15.7243 12.5229 15.7243 12.5457 15.721 12.5652C15.6104 14.8341 13.7907 16.6537 11.5218 16.7644C11.5023 16.7644 11.4795 16.7677 11.46 16.7709H11.3005H11.0238C9.22689 16.7709 7.50489 16.058 6.23536 14.7885L3.38054 11.9304C3.17872 11.7286 3.17872 11.3966 3.38054 11.1947C3.58236 10.9929 3.91439 10.9929 4.11622 11.1947L5.53874 12.6173C5.76335 12.8419 6.09864 12.907 6.39161 12.7865C6.68458 12.6661 6.87338 12.3796 6.87338 12.0639V4.79175C6.87338 4.50529 7.10775 4.27091 7.39421 4.27091C7.68067 4.27091 7.91504 4.50203 7.91504 4.78849V9.21883C7.91504 9.65177 8.26335 10.0001 8.69629 10.0001C9.12924 10.0001 9.47754 9.65177 9.47754 9.21883V4.795Z" fill="#5C5C5C"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.99839 1.66675C9.17483 1.66675 8.46193 2.14526 8.12339 2.83862C7.89553 2.75399 7.65139 2.70841 7.39423 2.70841C6.24514 2.70841 5.31089 3.64266 5.31089 4.79175V10.1791L5.223 10.0912C4.4092 9.27742 3.09084 9.27742 2.27704 10.0912C1.46324 10.905 1.46324 12.2234 2.27704 13.0372L5.13186 15.892C6.69436 17.4545 8.8135 18.3334 11.0238 18.3334H11.3005H11.5609C11.6097 18.3334 11.6586 18.3302 11.7074 18.3204C14.6924 18.1186 17.0785 15.7358 17.277 12.7507C17.2868 12.7019 17.2901 12.6531 17.2901 12.6042V6.87508C17.2901 5.72599 16.3558 4.79175 15.2067 4.79175C15.0277 4.79175 14.8519 4.81453 14.6859 4.85685V4.79175C14.6859 3.64266 13.7516 2.70841 12.6026 2.70841C12.3454 2.70841 12.1013 2.75399 11.8734 2.83862C11.5349 2.14526 10.822 1.66675 9.99839 1.66675ZM9.47756 4.795V4.79175V3.75008C9.47756 3.46362 9.71194 3.22925 9.99839 3.22925C10.2849 3.22925 10.5192 3.46362 10.5192 3.75008V4.78849V4.79175V9.21883C10.5192 9.65177 10.8675 10.0001 11.3005 10.0001C11.7334 10.0001 12.0817 9.65177 12.0817 9.21883V4.79175C12.0817 4.79175 12.0817 4.79175 12.0817 4.78849C12.0817 4.50203 12.3161 4.26766 12.6026 4.26766C12.889 4.26766 13.1234 4.50203 13.1234 4.78849V6.60815V6.61141V9.21558C13.1234 9.64852 13.4717 9.99683 13.9046 9.99683C14.3376 9.99683 14.6859 9.64852 14.6859 9.21558V6.87834V6.87508C14.6859 6.58862 14.9203 6.35425 15.2067 6.35425C15.4932 6.35425 15.7276 6.58862 15.7276 6.87508V12.5033C15.7243 12.5229 15.7243 12.5457 15.7211 12.5652C15.6104 14.8341 13.7907 16.6537 11.5218 16.7644C11.5023 16.7644 11.4795 16.7677 11.46 16.7709H11.3005H11.0238C9.22691 16.7709 7.5049 16.058 6.23537 14.7885L3.38055 11.9304C3.17873 11.7286 3.17873 11.3966 3.38055 11.1947C3.58238 10.9929 3.91441 10.9929 4.11623 11.1947L5.53876 12.6173C5.76337 12.8419 6.09865 12.907 6.39162 12.7865C6.68459 12.6661 6.87339 12.3796 6.87339 12.0639V4.79175C6.87339 4.50529 7.10777 4.27091 7.39423 4.27091C7.68068 4.27091 7.91506 4.50203 7.91506 4.78849V9.21883C7.91506 9.65177 8.26337 10.0001 8.69631 10.0001C9.12925 10.0001 9.47756 9.65177 9.47756 9.21883V4.795Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.20618 1.91333C13.2338 1.91338 16.4991 5.17868 16.4991 9.2063C16.4991 10.9499 15.8852 12.5492 14.8644 13.804L17.8673 16.8059C18.1602 17.0988 18.1602 17.5745 17.8673 17.8674C17.5744 18.1603 17.0987 18.1603 16.8058 17.8674L13.8038 14.8645C12.5491 15.8853 10.9498 16.4992 9.20618 16.4993C5.17855 16.4993 1.91326 13.2339 1.91321 9.2063C1.91321 5.17865 5.17852 1.91333 9.20618 1.91333ZM9.20618 3.41333C6.00695 3.41333 3.41321 6.00707 3.41321 9.2063C3.41326 12.4055 6.00698 14.9993 9.20618 14.9993C12.4053 14.9992 14.9991 12.4055 14.9991 9.2063C14.9991 6.0071 12.4054 3.41338 9.20618 3.41333ZM9.20618 5.29517C9.62017 5.29531 9.95602 5.63118 9.95618 6.04517V8.4563H12.3663C12.7805 8.4563 13.1163 8.79209 13.1163 9.2063C13.1163 9.62047 12.7805 9.9563 12.3663 9.9563H9.95618V12.3674C9.95592 12.7813 9.62011 13.1173 9.20618 13.1174C8.79212 13.1174 8.45644 12.7814 8.45618 12.3674V9.9563H6.04504C5.63089 9.95627 5.29509 9.62045 5.29504 9.2063C5.29504 8.7921 5.63086 8.45633 6.04504 8.4563H8.45618V6.04517C8.45634 5.63109 8.79206 5.29517 9.20618 5.29517Z" fill="#5C5C5C"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.20618 1.91333C13.2338 1.91338 16.4991 5.17868 16.4991 9.2063C16.4991 10.9499 15.8852 12.5492 14.8644 13.804L17.8673 16.8059C18.1602 17.0988 18.1602 17.5745 17.8673 17.8674C17.5744 18.1603 17.0987 18.1603 16.8058 17.8674L13.8038 14.8645C12.5491 15.8853 10.9498 16.4992 9.20618 16.4993C5.17855 16.4993 1.91326 13.2339 1.91321 9.2063C1.91321 5.17865 5.17852 1.91333 9.20618 1.91333ZM9.20618 3.41333C6.00695 3.41333 3.41321 6.00707 3.41321 9.2063C3.41326 12.4055 6.00698 14.9993 9.20618 14.9993C12.4053 14.9992 14.9991 12.4055 14.9991 9.2063C14.9991 6.0071 12.4054 3.41338 9.20618 3.41333ZM9.20618 5.29517C9.62017 5.29531 9.95602 5.63118 9.95618 6.04517V8.4563H12.3663C12.7805 8.4563 13.1163 8.79209 13.1163 9.2063C13.1163 9.62047 12.7805 9.9563 12.3663 9.9563H9.95618V12.3674C9.95592 12.7813 9.62011 13.1173 9.20618 13.1174C8.79212 13.1174 8.45644 12.7814 8.45618 12.3674V9.9563H6.04504C5.63089 9.95627 5.29509 9.62045 5.29504 9.2063C5.29504 8.7921 5.63086 8.45633 6.04504 8.4563H8.45618V6.04517C8.45634 5.63109 8.79206 5.29517 9.20618 5.29517Z" fill="#CECECE"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.20618 1.91333C13.2338 1.91338 16.4991 5.17868 16.4991 9.2063C16.4991 10.9499 15.8852 12.5492 14.8644 13.804L17.8673 16.8059C18.1602 17.0988 18.1602 17.5745 17.8673 17.8674C17.5744 18.1603 17.0987 18.1603 16.8058 17.8674L13.8038 14.8645C12.5491 15.8853 10.9498 16.4992 9.20618 16.4993C5.17855 16.4993 1.91326 13.2339 1.91321 9.2063C1.91321 5.17865 5.17852 1.91333 9.20618 1.91333ZM9.20618 3.41333C6.00695 3.41333 3.41321 6.00707 3.41321 9.2063C3.41326 12.4055 6.00698 14.9993 9.20618 14.9993C12.4053 14.9992 14.9991 12.4055 14.9991 9.2063C14.9991 6.0071 12.4054 3.41338 9.20618 3.41333ZM12.3663 8.4563C12.7805 8.4563 13.1163 8.79209 13.1163 9.2063C13.1163 9.62047 12.7805 9.9563 12.3663 9.9563H6.04504C5.63089 9.95627 5.29509 9.62045 5.29504 9.2063C5.29504 8.7921 5.63086 8.45633 6.04504 8.4563H12.3663Z" fill="#5C5C5C"/>
</svg>

After

Width:  |  Height:  |  Size: 936 B

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.20618 1.91333C13.2338 1.91338 16.4991 5.17868 16.4991 9.2063C16.4991 10.9499 15.8852 12.5492 14.8644 13.804L17.8673 16.8059C18.1602 17.0988 18.1602 17.5745 17.8673 17.8674C17.5744 18.1603 17.0987 18.1603 16.8058 17.8674L13.8038 14.8645C12.5491 15.8853 10.9498 16.4992 9.20618 16.4993C5.17855 16.4993 1.91326 13.2339 1.91321 9.2063C1.91321 5.17865 5.17852 1.91333 9.20618 1.91333ZM9.20618 3.41333C6.00695 3.41333 3.41321 6.00707 3.41321 9.2063C3.41326 12.4055 6.00698 14.9993 9.20618 14.9993C12.4053 14.9992 14.9991 12.4055 14.9991 9.2063C14.9991 6.0071 12.4054 3.41338 9.20618 3.41333ZM12.3663 8.4563C12.7805 8.4563 13.1163 8.79209 13.1163 9.2063C13.1163 9.62047 12.7805 9.9563 12.3663 9.9563H6.04504C5.63089 9.95627 5.29509 9.62045 5.29504 9.2063C5.29504 8.7921 5.63086 8.45633 6.04504 8.4563H12.3663Z" fill="#CECECE"/>
</svg>

After

Width:  |  Height:  |  Size: 936 B

View File

@@ -0,0 +1,26 @@
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1314_965)">
<path d="M151.1 99.8C151.1 114 145.3 126.8 136 136C126.9 145.1 114.3 150.6 100.4 150.6C86.6002 150.6 74.0002 145 64.8002 136C55.5002 126.8 49.7002 114 49.7002 99.8C49.7002 71.7 72.4002 49 100.4 49C128.4 49 151.1 71.8 151.1 99.8Z" fill="#F7F7F7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M100.4 50C72.9538 50 50.7002 72.251 50.7002 99.8C50.7002 113.721 56.3845 126.267 65.5017 135.287C74.5247 144.113 86.8764 149.6 100.4 149.6C114.032 149.6 126.378 144.208 135.293 135.293L135.297 135.289C144.415 126.269 150.1 113.722 150.1 99.8C150.1 72.3497 127.845 50 100.4 50ZM48.7002 99.8C48.7002 71.149 71.8466 48 100.4 48C128.955 48 152.1 71.2503 152.1 99.8C152.1 114.278 146.186 127.33 136.705 136.71C127.42 145.993 114.568 151.6 100.4 151.6C86.3251 151.6 73.4777 145.888 64.1009 136.715L64.0969 136.711C54.6151 127.331 48.7002 114.278 48.7002 99.8Z" fill="#DBDBDB"/>
<path d="M93.3692 54.2109L89.6083 67.4761L98.6108 73.7153L97.9777 88.9569L109.341 68.1032L104.063 63.8865L110.099 54.2109H116.875L137.375 76.5109L138.175 140.911C138.175 143.111 136.375 144.811 134.175 144.811H71.3751C69.175 144.811 67.375 143.011 67.375 140.911V58.111C67.375 55.9109 69.175 54.2109 71.3751 54.2109H93.3692Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M71.3751 55.2109C69.6939 55.2109 68.375 56.496 68.375 58.111V140.911C68.375 142.442 69.7108 143.811 71.3751 143.811H134.175C135.854 143.811 137.172 142.529 137.175 140.917C137.175 140.915 137.175 140.913 137.175 140.911L136.38 76.906L116.436 55.2109H110.654L105.382 63.6607L109.965 67.3219C110.344 67.6247 110.451 68.1558 110.219 68.5817L98.8558 89.4354C98.6334 89.8436 98.1589 90.0447 97.7108 89.9206C97.2627 89.7965 96.9593 89.38 96.9786 88.9154L97.5888 74.2237L89.0387 68.298C88.6874 68.0545 88.5297 67.6146 88.6463 67.2033L92.0463 55.2109H71.3751ZM66.375 58.111C66.375 55.3259 68.6561 53.2109 71.3751 53.2109H93.3692C93.6826 53.2109 93.9779 53.3579 94.1669 53.6079C94.3559 53.858 94.4168 54.1822 94.3313 54.4837L90.7654 67.0613L99.1804 72.8934C99.4621 73.0886 99.6241 73.4144 99.6099 73.7568L99.1551 84.706L108.061 68.3611L103.438 64.6677C103.042 64.351 102.946 63.7877 103.214 63.3572L109.25 53.6816C109.433 53.3889 109.754 53.2109 110.099 53.2109H116.875C117.155 53.2109 117.422 53.3282 117.611 53.5342L138.111 75.8342C138.278 76.0156 138.372 76.2521 138.375 76.4985L139.175 140.911C139.175 143.696 136.894 145.811 134.175 145.811H71.3751C68.6392 145.811 66.375 143.58 66.375 140.911V58.111Z" fill="#DBDBDB"/>
<path d="M146.7 66.4998C148.964 66.4998 150.8 64.6642 150.8 62.3998C150.8 60.1354 148.964 58.2998 146.7 58.2998C144.435 58.2998 142.6 60.1354 142.6 62.3998C142.6 64.6642 144.435 66.4998 146.7 66.4998Z" fill="#EBEBEB"/>
<path d="M152.7 50.4999C154.247 50.4999 155.5 49.2463 155.5 47.6999C155.5 46.1535 154.247 44.8999 152.7 44.8999C151.154 44.8999 149.9 46.1535 149.9 47.6999C149.9 49.2463 151.154 50.4999 152.7 50.4999Z" fill="#EBEBEB"/>
<path d="M51.3 66.3998C52.8464 66.3998 54.1 65.1462 54.1 63.5998C54.1 62.0534 52.8464 60.7998 51.3 60.7998C49.7536 60.7998 48.5 62.0534 48.5 63.5998C48.5 65.1462 49.7536 66.3998 51.3 66.3998Z" fill="#EBEBEB"/>
<path d="M34.2 120.4C37.0719 120.4 39.4 118.072 39.4 115.2C39.4 112.328 37.0719 110 34.2 110C31.3281 110 29 112.328 29 115.2C29 118.072 31.3281 120.4 34.2 120.4Z" fill="#EBEBEB"/>
<path d="M89.2291 67.7108H77.1291C76.4291 67.7108 75.8291 67.1108 75.8291 66.4108C75.8291 65.7108 76.4291 65.1108 77.1291 65.1108H89.2291C89.9291 65.1108 90.5291 65.7108 90.5291 66.4108C90.5291 67.1108 89.9291 67.7108 89.2291 67.7108Z" fill="#DBDBDB"/>
<path d="M88.7291 74.911H77.1291C76.4291 74.911 75.8291 74.311 75.8291 73.611C75.8291 72.911 76.4291 72.311 77.1291 72.311H88.6291C89.3291 72.311 89.9291 72.911 89.9291 73.611C89.9291 74.311 89.3291 74.911 88.7291 74.911Z" fill="#DBDBDB"/>
<path d="M116.029 54.2109V72.0109C116.029 74.5109 118.229 76.5109 120.729 76.5109H136.529" fill="#DBDBDB"/>
<path d="M89.9799 48.2657L86.1982 42.5732" stroke="#C2C2C2" stroke-width="2"/>
<path d="M100.065 45.4195L100.51 31" stroke="#C2C2C2" stroke-width="2"/>
<path d="M109.107 48.4L115.144 35.2974" stroke="#C2C2C2" stroke-width="2"/>
<path d="M88.5713 120.379C88.1513 120.379 87.8152 120.211 87.4792 119.959C86.8911 119.371 86.8911 118.363 87.4792 117.775C91.3437 113.994 96.4685 111.894 101.929 111.894C107.39 111.894 112.515 113.994 116.38 117.775C116.968 118.363 116.968 119.371 116.38 119.959C115.791 120.547 114.783 120.547 114.195 119.959C110.919 116.682 106.55 114.918 101.929 114.918C97.3086 114.918 92.94 116.682 89.6635 119.959C89.3274 120.211 88.9914 120.379 88.5713 120.379Z" fill="#C2C2C2"/>
<path d="M93.8641 97.8636L95.8804 95.8473C96.4685 95.2592 96.4685 94.2511 95.8804 93.663C95.2923 93.0749 94.2842 93.0749 93.6961 93.663L91.6798 95.6793L89.6635 93.663C89.0754 93.0749 88.0672 93.0749 87.4792 93.663C86.8911 94.2511 86.8911 95.2592 87.4792 95.8473L89.4955 97.8636L87.4792 99.8799C86.8911 100.468 86.8911 101.476 87.4792 102.064C87.8152 102.4 88.1513 102.484 88.5713 102.484C88.9914 102.484 89.3274 102.316 89.6635 102.064L91.6798 100.048L93.6961 102.064C94.0322 102.4 94.3682 102.484 94.7883 102.484C95.2083 102.484 95.5444 102.316 95.8804 102.064C96.4685 101.476 96.4685 100.468 95.8804 99.8799L93.8641 97.8636Z" fill="#C2C2C2"/>
<path d="M114.279 97.8636L116.295 95.8473C116.884 95.2592 116.884 94.2511 116.295 93.663C115.707 93.0749 114.699 93.0749 114.111 93.663L112.095 95.6793L110.079 93.663C109.49 93.0749 108.482 93.0749 107.894 93.663C107.306 94.2511 107.306 95.2592 107.894 95.8473L109.91 97.8636L107.894 99.8799C107.306 100.468 107.306 101.476 107.894 102.064C108.23 102.4 108.566 102.484 108.986 102.484C109.406 102.484 109.742 102.316 110.079 102.064L112.095 100.048L114.111 102.064C114.447 102.4 114.783 102.484 115.203 102.484C115.623 102.484 115.959 102.316 116.295 102.064C116.884 101.476 116.884 100.468 116.295 99.8799L114.279 97.8636Z" fill="#C2C2C2"/>
</g>
<defs>
<clipPath id="clip0_1314_965">
<rect width="200" height="160" fill="white" transform="translate(0 20)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.25 16H4.75V18.5H7.25V16ZM4.75 15C4.48478 15 4.23043 15.1054 4.04289 15.2929C3.85536 15.4804 3.75 15.7348 3.75 16V18.5C3.75 18.7652 3.85536 19.0196 4.04289 19.2071C4.23043 19.3946 4.48478 19.5 4.75 19.5H7.25C7.51522 19.5 7.76957 19.3946 7.95711 19.2071C8.14464 19.0196 8.25 18.7652 8.25 18.5V16C8.25 15.7348 8.14464 15.4804 7.95711 15.2929C7.76957 15.1054 7.51522 15 7.25 15H4.75ZM13.25 16H10.75V18.5H13.25V16ZM10.75 15C10.4848 15 10.2304 15.1054 10.0429 15.2929C9.85536 15.4804 9.75 15.7348 9.75 16V18.5C9.75 18.7652 9.85536 19.0196 10.0429 19.2071C10.2304 19.3946 10.4848 19.5 10.75 19.5H13.25C13.5152 19.5 13.7696 19.3946 13.9571 19.2071C14.1446 19.0196 14.25 18.7652 14.25 18.5V16C14.25 15.7348 14.1446 15.4804 13.9571 15.2929C13.7696 15.1054 13.5152 15 13.25 15H10.75ZM19.25 16H16.75V18.5H19.25V16ZM16.75 15C16.4848 15 16.2304 15.1054 16.0429 15.2929C15.8554 15.4804 15.75 15.7348 15.75 16V18.5C15.75 18.7652 15.8554 19.0196 16.0429 19.2071C16.2304 19.3946 16.4848 19.5 16.75 19.5H19.25C19.5152 19.5 19.7696 19.3946 19.9571 19.2071C20.1446 19.0196 20.25 18.7652 20.25 18.5V16C20.25 15.7348 20.1446 15.4804 19.9571 15.2929C19.7696 15.1054 19.5152 15 19.25 15H16.75ZM6.367 8.244C5.181 9.537 4.559 11.069 4.499 12.047C4.48654 12.2458 4.39562 12.4315 4.24624 12.5632C4.09687 12.695 3.90128 12.762 3.7025 12.7495C3.50372 12.737 3.31803 12.6461 3.18629 12.4967C3.05455 12.3474 2.98754 12.1518 3 11.953C3.087 10.563 3.894 8.719 5.26 7.23C6.646 5.72 8.673 4.5 11.25 4.5C13.823 4.5 15.886 5.717 17.29 7.226C18.671 8.71 19.5 10.572 19.5 12H18C18 11.06 17.4 9.546 16.192 8.248C15.007 6.974 13.32 6 11.25 6C9.184 6 7.535 6.97 6.367 8.244Z" fill="#262E30"/>
<path d="M20.25 12H14.25L20.25 6V12Z" fill="#262E30"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.25001 16H4.75001V18.5H7.25001V16ZM4.75001 15C4.48479 15 4.23044 15.1054 4.0429 15.2929C3.85537 15.4804 3.75001 15.7348 3.75001 16V18.5C3.75001 18.7652 3.85537 19.0196 4.0429 19.2071C4.23044 19.3946 4.48479 19.5 4.75001 19.5H7.25001C7.51522 19.5 7.76958 19.3946 7.95712 19.2071C8.14465 19.0196 8.25001 18.7652 8.25001 18.5V16C8.25001 15.7348 8.14465 15.4804 7.95712 15.2929C7.76958 15.1054 7.51522 15 7.25001 15H4.75001ZM13.25 16H10.75V18.5H13.25V16ZM10.75 15C10.4848 15 10.2304 15.1054 10.0429 15.2929C9.85537 15.4804 9.75001 15.7348 9.75001 16V18.5C9.75001 18.7652 9.85537 19.0196 10.0429 19.2071C10.2304 19.3946 10.4848 19.5 10.75 19.5H13.25C13.5152 19.5 13.7696 19.3946 13.9571 19.2071C14.1447 19.0196 14.25 18.7652 14.25 18.5V16C14.25 15.7348 14.1447 15.4804 13.9571 15.2929C13.7696 15.1054 13.5152 15 13.25 15H10.75ZM19.25 16H16.75V18.5H19.25V16ZM16.75 15C16.4848 15 16.2304 15.1054 16.0429 15.2929C15.8554 15.4804 15.75 15.7348 15.75 16V18.5C15.75 18.7652 15.8554 19.0196 16.0429 19.2071C16.2304 19.3946 16.4848 19.5 16.75 19.5H19.25C19.5152 19.5 19.7696 19.3946 19.9571 19.2071C20.1447 19.0196 20.25 18.7652 20.25 18.5V16C20.25 15.7348 20.1447 15.4804 19.9571 15.2929C19.7696 15.1054 19.5152 15 19.25 15H16.75ZM6.36701 8.244C5.18101 9.537 4.55901 11.069 4.49901 12.047C4.48654 12.2458 4.39562 12.4315 4.24625 12.5632C4.09688 12.695 3.90129 12.762 3.70251 12.7495C3.50373 12.737 3.31804 12.6461 3.1863 12.4967C3.05455 12.3474 2.98754 12.1518 3.00001 11.953C3.08701 10.563 3.89401 8.719 5.26001 7.23C6.64601 5.72 8.67301 4.5 11.25 4.5C13.823 4.5 15.886 5.717 17.29 7.226C18.671 8.71 19.5 10.572 19.5 12H18C18 11.06 17.4 9.546 16.192 8.248C15.007 6.974 13.32 6 11.25 6C9.18401 6 7.53501 6.97 6.36701 8.244Z" fill="#ACACAC"/>
<path d="M20.25 12H14.25L20.25 6V12Z" fill="#ACACAC"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.47995 16.1333H4.89662V18.7167H7.47995V16.1333ZM4.89662 15.1C4.62256 15.1 4.35973 15.2089 4.16594 15.4027C3.97215 15.5964 3.86328 15.8593 3.86328 16.1333V18.7167C3.86328 18.9907 3.97215 19.2536 4.16594 19.4473C4.35973 19.6411 4.62256 19.75 4.89662 19.75H7.47995C7.75401 19.75 8.01684 19.6411 8.21063 19.4473C8.40441 19.2536 8.51328 18.9907 8.51328 18.7167V16.1333C8.51328 15.8593 8.40441 15.5964 8.21063 15.4027C8.01684 15.2089 7.75401 15.1 7.47995 15.1H4.89662ZM13.68 16.1333H11.0966V18.7167H13.68V16.1333ZM11.0966 15.1C10.8226 15.1 10.5597 15.2089 10.3659 15.4027C10.1722 15.5964 10.0633 15.8593 10.0633 16.1333V18.7167C10.0633 18.9907 10.1722 19.2536 10.3659 19.4473C10.5597 19.6411 10.8226 19.75 11.0966 19.75H13.68C13.954 19.75 14.2168 19.6411 14.4106 19.4473C14.6044 19.2536 14.7133 18.9907 14.7133 18.7167V16.1333C14.7133 15.8593 14.6044 15.5964 14.4106 15.4027C14.2168 15.2089 13.954 15.1 13.68 15.1H11.0966ZM19.88 16.1333H17.2966V18.7167H19.88V16.1333ZM17.2966 15.1C17.0226 15.1 16.7597 15.2089 16.5659 15.4027C16.3722 15.5964 16.2633 15.8593 16.2633 16.1333V18.7167C16.2633 18.9907 16.3722 19.2536 16.5659 19.4473C16.7597 19.6411 17.0226 19.75 17.2966 19.75H19.88C20.154 19.75 20.4168 19.6411 20.6106 19.4473C20.8044 19.2536 20.9133 18.9907 20.9133 18.7167V16.1333C20.9133 15.8593 20.8044 15.5964 20.6106 15.4027C20.4168 15.2089 20.154 15.1 19.88 15.1H17.2966ZM6.56752 8.1188C5.34198 9.4549 4.69925 11.038 4.63725 12.0486C4.62437 12.254 4.53042 12.4458 4.37607 12.582C4.22172 12.7181 4.01961 12.7874 3.8142 12.7745C3.6088 12.7616 3.41692 12.6677 3.28078 12.5133C3.14465 12.3589 3.0754 12.1568 3.08828 11.9514C3.17818 10.5151 4.01208 8.60963 5.42362 7.071C6.85582 5.51067 8.95038 4.25 11.6133 4.25C14.2721 4.25 16.4038 5.50757 17.8546 7.06687C19.2817 8.60033 20.1383 10.5244 20.1383 12H18.5883C18.5883 11.0287 17.9683 9.4642 16.72 8.12293C15.4955 6.80647 13.7523 5.8 11.6133 5.8C9.47842 5.8 7.77445 6.80233 6.56752 8.1188Z" fill="#262E30"/>
<path d="M20.9133 12H14.7133L20.9133 5.79999V12Z" fill="#262E30"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,3 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.2254 6.20744C14.6554 6.20744 15.0013 5.86151 15.0013 5.43151V0.77593C15.0013 0.345936 14.6554 0 14.2254 0C13.7954 0 13.4495 0.345936 13.4495 0.77593V3.3656L12.8029 2.60583C11.4773 1.01518 9.47605 0 7.24202 0C3.24274 0 0 3.24274 0 7.24202C0 11.2413 3.24274 14.484 7.24202 14.484C8.87147 14.484 10.3781 13.9441 11.5872 13.0356C11.9299 12.777 11.9978 12.292 11.7424 11.9493C11.487 11.6066 10.9988 11.5387 10.6561 11.7941C9.7056 12.5086 8.52553 12.9322 7.24202 12.9322C4.0995 12.9322 1.55186 10.3845 1.55186 7.24202C1.55186 4.0995 4.0995 1.55186 7.24202 1.55186C8.99756 1.55186 10.5688 2.34719 11.6131 3.60161L11.6163 3.60808L12.5086 4.65558H9.56981C9.13981 4.65558 8.79388 5.00152 8.79388 5.43151C8.79388 5.86151 9.13981 6.20744 9.56981 6.20744H14.2254Z" fill="#009688"/>
</svg>

After

Width:  |  Height:  |  Size: 885 B

View File

@@ -4,6 +4,8 @@ project(libslic3r_gui)
include(PrecompiledHeader)
set(SLIC3R_GUI_SOURCES
GUI/Widgets/AnimaController.hpp
GUI/Widgets/AnimaController.cpp
Config/Snapshot.cpp
Config/Snapshot.hpp
Config/Version.cpp
@@ -371,6 +373,11 @@ set(SLIC3R_GUI_SOURCES
GUI/SavePresetDialog.hpp
GUI/SceneRaycaster.cpp
GUI/SceneRaycaster.hpp
GUI/PartSkipCommon.hpp
GUI/PartSkipDialog.cpp
GUI/PartSkipDialog.hpp
GUI/SkipPartCanvas.cpp
GUI/SkipPartCanvas.hpp
GUI/Search.cpp
GUI/Search.hpp
GUI/Selection.cpp

View File

@@ -535,6 +535,13 @@ bool MachineObject::is_lan_mode_printer() const
return result;
}
std::string MachineObject::convertToIp(long long ip)
{
std::stringstream ss;
ss << ((ip >> 0) & 0xFF) << "." << ((ip >> 8) & 0xFF) << "." << ((ip >> 16) & 0xFF) << "." << ((ip >> 24) & 0xFF);
return ss.str();
}
PrinterSeries MachineObject::get_printer_series() const
{
std::string series = DeviceManager::get_printer_series(printer_type);
@@ -1853,6 +1860,17 @@ int MachineObject::command_control_fan_val(FanType fan_type, int val)
}
int MachineObject::command_task_partskip(std::vector<int> part_ids)
{
BOOST_LOG_TRIVIAL(trace) << "command_task_partskip: ";
json j;
j["print"]["command"] = "skip_objects";
j["print"]["obj_list"] = part_ids;
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
return this->publish_json(j.dump(), 1);
}
int MachineObject::command_task_abort()
{
BOOST_LOG_TRIVIAL(trace) << "command_task_abort: ";
@@ -2723,7 +2741,7 @@ void MachineObject::reset()
vt_tray.reset();
subtask_ = nullptr;
m_partskip_ids.clear();
}
void MachineObject::set_print_state(std::string status)
@@ -2959,6 +2977,29 @@ int MachineObject::parse_json(std::string payload, bool key_field_only)
print_json.load_compatible_settings(printer_type, "");
print_json.diff2all_base_reset(j_pre);
}
if (j_pre["print"].contains("s_obj")){
if(j_pre["print"]["s_obj"].is_array()){
m_partskip_ids.clear();
for(auto it=j_pre["print"]["s_obj"].begin(); it!=j_pre["print"]["s_obj"].end(); it++){
m_partskip_ids.push_back(it.value().get<int>());
}
}
}
}
}
if (j_pre["print"].contains("plate_idx")){ // && m_plate_index == -1
if (j_pre["print"]["plate_idx"].is_number())
{
m_plate_index = j_pre["print"]["plate_idx"].get<int>();
}
else if (j_pre["print"]["plate_idx"].is_string())
{
try
{
m_plate_index = std::stoi(j_pre["print"]["plate_idx"].get<std::string>());
}
catch (...) { BOOST_LOG_TRIVIAL(error) << "parse_json: failed to convert plate_idx to int"; }
}
}
}
@@ -3423,8 +3464,24 @@ int MachineObject::parse_json(std::string payload, bool key_field_only)
if (jj["net"].contains("conf")) {
network_wired = (jj["net"]["conf"].get<int>() & (0x1)) != 0;
}
if (jj["net"].contains("info")) {
for (auto info_item = jj["net"]["info"].begin(); info_item != jj["net"]["info"].end(); info_item++) {
if (info_item->contains("ip")) {
auto tmp_dev_ip = (*info_item)["ip"].get<int64_t>();
if (tmp_dev_ip == 0)
continue ;
else {
set_dev_ip(convertToIp(tmp_dev_ip));
}
} else {
break;
}
}
}
}
}
#pragma endregion
#pragma region online
@@ -5170,6 +5227,7 @@ void MachineObject::update_slice_info(std::string project_id, std::string profil
if (plate_idx >= 0) {
plate_index = plate_idx;
this->m_plate_index = plate_idx;
}
else {
std::string subtask_json;
@@ -5232,8 +5290,7 @@ void MachineObject::update_slice_info(std::string project_id, std::string profil
BOOST_LOG_TRIVIAL(error) << "task_info: get subtask id failed!";
}
}
this->m_plate_index = plate_index;
// this->m_plate_index = plate_index;
});
}
}
@@ -5562,6 +5619,8 @@ void MachineObject::parse_new_info(json print)
is_support_nozzle_blob_detection = get_flag_bits(fun, 13);
is_support_upgrade_kit = get_flag_bits(cfg, 14);
is_support_command_homing = get_flag_bits(fun, 32);
is_support_brtc = get_flag_bits(fun, 31);
is_support_partskip = get_flag_bits(fun, 49);
}
/*aux*/

View File

@@ -509,6 +509,7 @@ public:
int subscribe_counter{3};
std::string dev_connection_type; /* lan | cloud */
std::string connection_type() { return dev_connection_type; }
std::string dev_connection_name; /* lan | eth */
void set_dev_ip(std::string ip) {dev_ip = ip;}
std::string get_ftp_folder();
@@ -520,6 +521,7 @@ public:
void erase_user_access_code();
std::string get_user_access_code() const;
bool is_lan_mode_printer() const;
std::string convertToIp(long long ip);
//PRINTER_TYPE printer_type = PRINTER_3DPrinter_UKNOWN;
std::string printer_type; /* model_id */
@@ -831,6 +833,9 @@ public:
int xcam_filament_tangle_detect_count = 0;
int ams_print_option_count = 0;
// part skip
std::vector<int> m_partskip_ids;
//supported features
bool is_support_chamber_edit{false};
bool is_support_extrusion_cali{false};
@@ -870,6 +875,8 @@ public:
bool is_support_agora{false};
bool is_support_upgrade_kit{false};
bool is_support_command_homing { false };// fun[32]
bool is_support_brtc{false}; // fun[31], support tcp and upload protocol
bool is_support_partskip{false};
bool installed_upgrade_kit{false};
int nozzle_max_temperature = -1;
@@ -952,6 +959,7 @@ public:
int command_control_fan_val(FanType fan_type, int val);
int command_task_abort();
/* cancelled the job_id */
int command_task_partskip(std::vector<int> part_ids);
int command_task_cancel(std::string job_id);
int command_task_pause();
int command_task_resume();

View File

@@ -297,7 +297,12 @@ void MediaFilePanel::SetMachineObject(MachineObject* obj)
case PrinterFileSystem::Initializing: icon = m_bmp_loading; msg = _L("Initializing..."); break;
case PrinterFileSystem::Connecting: icon = m_bmp_loading; msg = _L("Connecting..."); break;
case PrinterFileSystem::Failed: icon = m_bmp_failed; if (extra != 1) msg = _L("Please check the network and try again. You can restart or update the printer if the issue persists."); break;
case PrinterFileSystem::ListSyncing: icon = m_bmp_loading; msg = _L("Loading file list..."); break;
case PrinterFileSystem::ListSyncing: {
icon = m_bmp_loading;
msg = _L("Loading file list...");
fs->ListAllFiles();
break;
}
case PrinterFileSystem::ListReady: icon = extra == 0 ? m_bmp_empty : m_bmp_failed; msg = extra == 0 ? _L("No files") : _L("Load failed"); break;
}
int err = fs->GetLastError();

View File

@@ -0,0 +1,18 @@
#ifndef PARTSKIPCOMMON_H
#define PARTSKIPCOMMON_H
namespace Slic3r { namespace GUI {
enum PartState {
psUnCheck,
psChecked,
psSkipped
};
typedef std::vector<std::pair<int, PartState>> PartsInfo;
}}
#endif // PARTSKIPCOMMON_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,163 @@
#include <wx/panel.h>
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/sizer.h>
#include <wx/gbsizer.h>
#include <wx/webrequest.h>
#include <wx/control.h>
#include <wx/dcclient.h>
#include <wx/display.h>
#include <wx/mstream.h>
#include <wx/sstream.h>
#include <wx/zstream.h>
#include <wx/window.h>
#include <wx/dcgraph.h>
#include <wx/simplebook.h>
#include "Widgets/Label.hpp"
#include "Widgets/CheckBox.hpp"
#include "Widgets/Button.hpp"
#include "Widgets/AnimaController.hpp"
#include "DeviceManager.hpp"
#include "PartSkipCommon.hpp"
#include "Printer/PrinterFileSystem.h"
#include "I18N.hpp"
#include "GUI_Utils.hpp"
namespace Slic3r { namespace GUI {
class SkipPartCanvas;
enum URL_STATE {
URL_TCP,
URL_TUTK,
};
class PartSkipConfirmDialog : public DPIDialog
{
private:
protected:
Label *m_msg_label;
Label *m_tip_label;
Button *m_apply_button;
public:
PartSkipConfirmDialog(wxWindow *parent);
~PartSkipConfirmDialog();
void on_dpi_changed(const wxRect &suggested_rect);
Button *GetConfirmButton();
void SetMsgLabel(wxString msg);
void SetTipLabel(wxString msg);
bool Show(bool show);
};
class PartSkipDialog : public DPIDialog
{
public:
PartSkipDialog(wxWindow *parent);
~PartSkipDialog();
void on_dpi_changed(const wxRect &suggested_rect);
bool Show(bool show);
void UpdatePartsStateFromPrinter(MachineObject *obj_);
void SetSimplebookPage(int page);
void InitSchedule(MachineObject *obj_);
void InitDialogUI();
int GetAllSkippedPartsNum();
MachineObject *m_obj{nullptr};
wxSimplebook *m_simplebook;
wxPanel *m_book_third_panel;
wxPanel *m_book_second_panel;
wxPanel *m_book_first_panel;
SkipPartCanvas *m_canvas;
Button *m_zoom_in_btn;
Button *m_zoom_out_btn;
Button *m_switch_drag_btn;
CheckBox *m_all_checkbox;
Button *m_percent_label;
Label *m_all_label;
wxPanel *m_line;
wxPanel *m_line_top;
wxScrolledWindow *m_list_view;
wxPanel *m_dlg_placeholder;
Label *m_cnt_label;
Label *m_tot_label;
Button *m_apply_btn;
Label *m_loading_label;
Label *m_retry_label;
ScalableBitmap *m_retry_icon;
wxStaticBitmap *m_retry_bitmap;
wxBoxSizer *m_sizer;
wxBoxSizer *m_dlg_sizer;
wxBoxSizer *m_dlg_content_sizer;
wxBoxSizer *m_dlg_btn_sizer;
wxBoxSizer *m_canvas_sizer;
wxBoxSizer *m_canvas_btn_sizer;
wxBoxSizer *m_list_sizer;
wxBoxSizer *m_scroll_sizer;
wxBoxSizer *m_book_first_sizer;
wxBoxSizer *m_book_second_sizer;
wxBoxSizer *m_book_second_btn_sizer;
Button *m_second_retry_btn;
AnimaIcon *m_loading_icon;
private:
int m_plate_idx{-1};
int m_zoom_percent{100};
bool m_is_drag{false};
bool m_print_lock{true};
bool m_enable_apply_btn{false};
bool is_model_support_partskip{false};
std::map<uint32_t, PartState> m_parts_state;
std::map<uint32_t, std::string> m_parts_name;
std::vector<int> m_partskip_ids;
enum URL_STATE m_url_state = URL_STATE::URL_TCP;
PartsInfo GetPartsInfo();
bool is_drag_mode();
boost::shared_ptr<PrinterFileSystem> m_file_sys;
bool m_file_sys_result{false};
std::string m_timestamp;
std::string m_tmp_path;
std::vector<string> m_local_paths;
std::vector<string> m_target_paths;
std::string create_tmp_path();
bool is_local_file_existed(const std::vector<string> &local_paths);
void DownloadPartsFile();
void OnFileSystemEvent(wxCommandEvent &event);
void OnFileSystemResult(wxCommandEvent &event);
void fetchUrl(boost::weak_ptr<PrinterFileSystem> wfs);
void OnZoomIn(wxCommandEvent &event);
void OnZoomOut(wxCommandEvent &event);
void OnSwitchDrag(wxCommandEvent &event);
void OnZoomPercent(wxCommandEvent &event);
void UpdatePartsStateFromCanvas(wxCommandEvent &event);
void UpdateZoomPercent();
void UpdateCountLabel();
void UpdateDialogUI();
void UpdateApplyButtonStatus();
bool IsAllChecked();
bool IsAllCancled();
void OnRetryButton(wxCommandEvent &event);
void OnAllCheckbox(wxCommandEvent &event);
void OnApplyDialog(wxCommandEvent &event);
};
}} // namespace Slic3r::GUI

View File

@@ -39,18 +39,31 @@ wxDEFINE_EVENT(EVT_FILE_CHANGED, wxCommandEvent);
wxDEFINE_EVENT(EVT_SELECT_CHANGED, wxCommandEvent);
wxDEFINE_EVENT(EVT_THUMBNAIL, wxCommandEvent);
wxDEFINE_EVENT(EVT_DOWNLOAD, wxCommandEvent);
wxDEFINE_EVENT(EVT_RAMDOWNLOAD, wxCommandEvent);
wxDEFINE_EVENT(EVT_MEDIA_ABILITY_CHANGED, wxCommandEvent);
wxDEFINE_EVENT(EVT_UPLOADING, wxCommandEvent);
wxDEFINE_EVENT(EVT_UPLOAD_CHANGED, wxCommandEvent);
wxDEFINE_EVENT(EVT_FILE_CALLBACK, wxCommandEvent);
static wxBitmap default_thumbnail;
static std::map<int, std::string> error_messages = {
{PrinterFileSystem::ERROR_PIPE, L("Reconnecting the printer, the operation cannot be completed immediately, please try again later.")},
{PrinterFileSystem::ERROR_RES_BUSY, L("The device cannot handle more conversations. Please retry later.")},
{PrinterFileSystem::FILE_NO_EXIST, L("File does not exist.")},
{PrinterFileSystem::FILE_CHECK_ERR, L("File checksum error. Please retry.")},
{PrinterFileSystem::FILE_TYPE_ERR, L("Not supported on the current printer version.")},
{PrinterFileSystem::STORAGE_UNAVAILABLE, L("Storage unavailable, insert SD card.")}
{PrinterFileSystem::ERROR_PIPE, L("Reconnecting the printer, the operation cannot be completed immediately, please try again later.")},
{PrinterFileSystem::ERROR_RES_BUSY, L("The device cannot handle more conversations. Please retry later.")},
{PrinterFileSystem::ERROR_TIME_OUT, L("Timeout, please try again.")},
{PrinterFileSystem::FILE_NO_EXIST, L("File does not exist.")},
{PrinterFileSystem::FILE_CHECK_ERR, L("File checksum error. Please retry.")},
{PrinterFileSystem::FILE_TYPE_ERR, L("Not supported on the current printer version.")},
{PrinterFileSystem::STORAGE_UNAVAILABLE, L("Please check if the SD card is inserted into the printer.\nIf it still cannot be read, you can try formatting the SD card.")},
{PrinterFileSystem::API_VERSION_UNSUPPORT, L("The firmware version of the printer is too low. Please update the firmware and try again.")},
{PrinterFileSystem::FILE_EXIST, L("The file already exists, do you want to replace it?")},
{PrinterFileSystem::STORAGE_SPACE_NOT_ENOUGH, L("Insufficient storage space, please clear the space and try again.")},
{PrinterFileSystem::FILE_CREATE_ERR, L("File creation failed, please try again.")},
{PrinterFileSystem::FILE_WRITE_ERR, L("File write failed, please try again.")},
{PrinterFileSystem::MD5_COMPARE_ERR, L("MD5 verification failed, please try again.")},
{PrinterFileSystem::FILE_RENAME_ERR, L("File renaming failed, please try again.")},
{PrinterFileSystem::SEND_ERR, L("File upload failed, please try again.")}
};
struct StaticBambuLib : BambuLib {
@@ -117,7 +130,6 @@ void PrinterFileSystem::SetFileType(FileType type, std::string const &storage)
return;
m_status = Status::ListSyncing;
SendChangedEvent(EVT_STATUS_CHANGED, m_status);
ListAllFiles();
}
void PrinterFileSystem::SetGroupMode(GroupMode mode)
@@ -248,6 +260,156 @@ struct PrinterFileSystem::Download : Progress
boost::uuids::detail::md5 boost_md5;
};
struct PrinterFileSystem::Upload : Progress
{
std::string error;
boost::uint32_t frag_id{0};
MD5_CTX ctx;
boost::filesystem::ifstream ifs;
};
void PrinterFileSystem::GetPickImages(const std::vector<std::string> &local_paths, const std::vector<std::string> &targetpaths)
{
m_download_states.clear();
GetPickImage(1, local_paths[0], targetpaths[0]);
GetPickImage(2, local_paths[1], targetpaths[1]);
GetPickImage(3, local_paths[2], targetpaths[2]);
}
void PrinterFileSystem::GetPickImage(int id, const std::string &local_path, const std::string &targetpath)
{
json j;
j["sequence_id"] = id;
j["version"] = 1;
j["peer_host"] = "studio";
j["command"] = "get_project_file";
j["file_rel_path"] = targetpath;
std::string param = j.dump();
DownloadRamFile(16, local_path, param);
}
void PrinterFileSystem::DownloadRamFile(int index, const std::string &local_path, const std::string & param)
{
std::shared_ptr<Download> download(new Download);
download->local_path = local_path;
json req;
req["path"] = "mem:/" + std::to_string(index);
req["offset"] = 0;
req["mem_dl_param_size"] = param.size();
m_download_seq = SendRequest<Progress>(
FILE_DOWNLOAD, req,
[download](json const &resp, Progress &prog, unsigned char const *data) -> int {
size_t size = resp.value("size", 0);
prog.size = resp["offset"];
prog.total = resp["total"];
if (resp.contains("mem_dl_param_size")) {
size_t s = resp["mem_dl_param_size"].get<size_t>();
std::string json_str(reinterpret_cast<const char *>(data), s);
// OutputDebugStringA(json_str.c_str());
// OutputDebugStringA("\n");
json mem_dl_json = json::parse(json_str);
// download->mem_dl_param_size = size;
if (!mem_dl_json.contains("result") || mem_dl_json["result"] == 1 ) {
wxLogWarning("Download failed: result = 1");
return ERROR_JSON;
}
if(mem_dl_json.contains("size") && mem_dl_json["size"] == 0 )
return FILE_SIZE_ERR;
return CONTINUE;
}
if (prog.size == 0 ) {
download->ofs.open(download->local_path, std::ios::binary);
if (!download->ofs) {
download->error = last_system_error();
wxLogWarning("DownloadImageFromRam open error: %s\n", wxString::FromUTF8(download->error));
return FILE_OPEN_ERR;
}
}
download->ofs.write(reinterpret_cast<const char *>(data), size);
if (!download->ofs) {
download->error = last_system_error();
wxLogWarning("DownloadImageFromRam write error: %s\n", wxString::FromUTF8(download->error));
return FILE_READ_WRITE_ERR;
}
download->boost_md5.process_bytes(data, size);
prog.size += size;
download->total = prog.total;
download->size = prog.size;
if (prog.size < prog.total) {
return 0;
}
download->ofs.close();
std::string md5 = resp["file_md5"];
boost::uuids::detail::md5::digest_type digest;
download->boost_md5.get_digest(digest);
for (int i = 0; i < 4; ++i) digest[i] = boost::endian::endian_reverse(digest[i]);
std::string str_md5;
const auto char_digest = reinterpret_cast<const char *>(&digest[0]);
boost::algorithm::hex(char_digest, char_digest + sizeof(digest), std::back_inserter(str_md5));
if (!boost::iequals(str_md5, md5)) {
wxLogWarning("DownloadImageFromRam checksum error: %s != %s\n", str_md5, md5);
boost::system::error_code ec;
boost::filesystem::rename(download->local_path, download->local_path + ".tmp", ec);
return FILE_CHECK_ERR;
}
return SUCCESS;
},
[this, download](int result, Progress const &data) {
//OutputDebugStringA(std::to_string(result).c_str());
//OutputDebugStringA("\n");
if (result == CONTINUE) { return; }
std::string msg;
if (result == SUCCESS) {
if (std::filesystem::exists(download->local_path)) {
m_download_states.emplace_back(true);
BOOST_LOG_TRIVIAL(info) <<"DownloadImageFromRam finished: " << download->local_path << "result = " << result;
}else{
m_download_states.emplace_back(false);
BOOST_LOG_TRIVIAL(warning) <<"DownloadImageFromRam finished, but file not exist: " << download->local_path << "result = " << result;
}
} else if (result != CONTINUE) {
m_download_states.emplace_back(false);
BOOST_LOG_TRIVIAL(warning) << "DownloadImageFromRam failed: " << download->error << "result = " << result;
}
if(m_download_states.size() == 3){
if(m_download_states[0] && m_download_states[1] && m_download_states[2]){
SendChangedEvent(EVT_RAMDOWNLOAD, SUCCESS);
}else{
// FILE_NO_EXIST is not really error_code
SendChangedEvent(EVT_RAMDOWNLOAD, FILE_NO_EXIST);
}
}else{
BOOST_LOG_TRIVIAL(warning) << "m_download_states current size is : " << m_download_states.size();
}
},param);
}
void PrinterFileSystem::SendExistedFile(){
SendChangedEvent(EVT_RAMDOWNLOAD, SUCCESS);
}
void PrinterFileSystem::SendConnectFail(){
SendChangedEvent(EVT_RAMDOWNLOAD, ERROR_PIPE);
}
void PrinterFileSystem::DownloadFiles(size_t index, std::string const &path)
{
if (index == (size_t) -1) {
@@ -281,6 +443,10 @@ void PrinterFileSystem::DownloadFiles(size_t index, std::string const &path)
DownloadNextFile();
}
void PrinterFileSystem::DownloadCheckFiles(std::string const &path)
{
for (size_t i = 0; i < m_file_list.size(); ++i) {
@@ -524,6 +690,16 @@ void PrinterFileSystem::Stop(bool quit)
m_cond.notify_all();
}
void PrinterFileSystem::SetUploadFile(const std::string &path, const std::string &name, const std::string &select_storage)
{
if (!m_upload_file) {
m_upload_file = std::make_unique<UploadFile>();
}
m_upload_file->path = path;
m_upload_file->name = name;
m_upload_file->select_storage = select_storage;
}
void PrinterFileSystem::BuildGroups()
{
m_group_year.clear();
@@ -1030,21 +1206,216 @@ void PrinterFileSystem::DumpLog(void * thiz, int, tchar const *msg)
static_cast<PrinterFileSystem*>(thiz)->Bambu_FreeLogMsg(msg);
}
boost::uint32_t PrinterFileSystem::SendRequest(int type, json const &req, callback_t2 const &callback)
boost::uint32_t PrinterFileSystem::RequestMediaAbility(int api_version)
{
json req;
req["peer"] = "studio";
req["api_version"] = api_version;
return SendRequest<MediaAbilityList>(
REQUEST_MEDIA_ABILITY, req, [this](const json &resp, MediaAbilityList &list, auto) -> int {
json abliity_list = resp["storage"];
list = abliity_list.get<MediaAbilityList>();
return 0;
},
[this](int result, MediaAbilityList list){
if (result != 0) {
m_last_error = result;
m_media_ability_list.clear();
SendChangedEvent(EVT_MEDIA_ABILITY_CHANGED, RequestMediaAbilityStatus::S_FAILED, "", m_last_error);
return result;
}
m_media_ability_list.swap(list);
SendChangedEvent(EVT_MEDIA_ABILITY_CHANGED, RequestMediaAbilityStatus::S_SUCCESS);
return 0;
});
}
void PrinterFileSystem::RequestUploadFile()
{
json req;
req["type"] = "model";
req["storage"] = m_upload_file->select_storage;
req["path"] = m_upload_file->name;
m_upload_file->upload = std::make_unique<Upload>();
boost::filesystem::path path = boost::filesystem::path(m_upload_file->path);
boost::system::error_code ec;
boost::uint32_t file_size = boost::filesystem::file_size(path, ec);
req["total"] = file_size;
m_upload_file->size = file_size;
m_upload_file->upload->total = file_size;
m_upload_seq = SendRequest(
FILE_UPLOAD, req,
[this](int result, const json& resp, auto) -> int{
if (result != SUCCESS && result != CONTINUE && result != FILE_EXIST) {
std::string error_msg = "";
if (result == ERROR_CANCEL) {
error_msg = L("User cancels task.");
} else if (result == FILE_READ_WRITE_ERR || result == FILE_OPEN_ERR) {
error_msg = L("Failed to read file, please try again.");
}
wxLogWarning("PrinterFileSystem::UploadFile error: %d\n", result);
SendChangedEvent(EVT_UPLOAD_CHANGED, FF_UPLOADCANCEL, error_msg, result);
} else if (result == SUCCESS) {
SendChangedEvent(EVT_UPLOADING, 100);
SendChangedEvent(EVT_UPLOAD_CHANGED, FF_UPLOADDONE);
} else if (result == CONTINUE || result == FILE_EXIST) {
if (m_upload_file) {
m_upload_file->chunk_size = resp["chunk_size"];
m_upload_file->upload->size = resp["offset"];
m_upload_file->flags |= FF_UPLOADING;
}
{
boost::unique_lock l(m_mutex);
auto cb = [this, upload_file = m_upload_file, seq = m_upload_seq](std::string &msg) -> int {
return UploadFileTask(upload_file, seq, msg);
};
m_produce_message_cb_map[m_upload_seq] = cb;
}
return CONTINUE;
}
// reset m_upload_file
if (m_upload_file) {
m_upload_file.reset();
}
return result;
});
}
int PrinterFileSystem::UploadFileTask(std::shared_ptr<UploadFile> upload_file, boost::uint64_t seq, std::string &msg)
{
if (!upload_file)
return FILE_OPEN_ERR;
if (!(upload_file->flags & FF_UPLOADING))
return FILE_OPEN_ERR;
auto &upload = upload_file->upload;
if (!upload->ifs.is_open()) {
upload->ifs.open(upload_file->path, std::ios::binary);
if (!upload_file->upload->ifs) {
wxLogWarning("PrinterFileSystem::UploadFile open error: %s\n", wxString::FromUTF8(upload_file->path));
return FILE_OPEN_ERR;
}
MD5_Init(&upload->ctx);
}
const boost::uint32_t buffer_size = upload_file->chunk_size * 1024;
char *buffer = new char[buffer_size];
upload->ifs.seekg(upload->size, std::ios::beg);
upload->ifs.read(buffer, buffer_size);
boost::int32_t read_size = upload->ifs.gcount();
if (read_size <= 0) {
wxLogWarning("PrinterFileSystem::Upload read error.\n");
upload->ifs.close();
if (buffer) {
delete[] buffer;
buffer = nullptr;
}
return FILE_READ_WRITE_ERR;
}
json req;
req["frag_id"] = upload->frag_id;
req["offset"] = upload->size;
req["size"] = read_size;
MD5_Update(&upload->ctx, buffer, read_size);
upload->size += read_size;
if (upload->size == upload->total) {
unsigned char digest[16];
MD5_Final(digest, &upload->ctx);
char md5_str[33];
for (int j = 0; j < 16; j++) { sprintf(&md5_str[j * 2], "%02X", (unsigned int) digest[j]); }
std::string md5_out = std::string(md5_str);
std::transform(md5_out.begin(), md5_out.end(), md5_out.begin(), ::tolower);
req["file_md5"] = md5_out;
// OutputDebugStringA(md5_out.c_str());
// OutputDebugStringA("\n");
}
if (m_upload_file && m_upload_file->flags & FF_UPLOADING) {
upload->frag_id++;
upload->progress = upload->size * 100 / upload->total;
int progress = upload->progress == 100 ? 99 : upload->progress;
SendChangedEvent(EVT_UPLOADING, progress);
}
json root;
root["cmdtype"] = FILE_UPLOAD;
root["sequence"] = seq;
root["req"] = req;
std::ostringstream oss;
oss << root;
oss << "\n\n";
oss << std::string(buffer, read_size);
msg = oss.str();
if (buffer) {
delete[] buffer;
buffer = nullptr;
}
if (upload->size == upload->total) {
upload->ifs.close();
return SUCCESS;
}
return CONTINUE;
}
PrinterFileSystem::MediaAbilityList PrinterFileSystem::GetMediaAbilityList() const
{
return m_media_ability_list;
}
void PrinterFileSystem::CancelUploadTask(bool send_cancel_req)
{
if (!m_upload_file)
return;
if (send_cancel_req) {
CancelRequest(m_upload_seq);
} else {
CancelRequests2({m_upload_seq});
}
}
boost::uint32_t PrinterFileSystem::SendRequest(int type, json const &req, callback_t2 const &callback,const std::string& param)
{
if (m_session.tunnel == nullptr) {
Retry();
callback(ERROR_PIPE, json(), nullptr);
return 0;
}
boost::uint32_t seq = m_sequence + m_callbacks.size();
boost::uint32_t seq = m_sequence + m_callbacks.size();
json root;
root["cmdtype"] = type;
root["sequence"] = seq;
root["req"] = req;
std::ostringstream oss;
oss << root;
auto msg = oss.str();
if (!param.empty()) {
oss << "\n\n";
oss << param;
}
// OutputDebugStringA(oss.str().c_str());
// OutputDebugStringA("\n");
auto msg = oss.str();
boost::unique_lock l(m_mutex);
m_messages.push_back(msg);
m_callbacks.push_back(callback);
@@ -1085,13 +1456,15 @@ void PrinterFileSystem::CancelRequests2(std::vector<boost::uint32_t> const &seqs
for (auto &f : seqs) {
boost::uint32_t seq = f;
seq -= m_sequence;
if (size_t(seq) >= m_callbacks.size())
continue;
if (size_t(seq) >= m_callbacks.size()) continue;
auto &c = m_callbacks[seq];
if (c == nullptr)
continue;
if (c == nullptr) continue;
callbacks.emplace_back(f, c);
c = nullptr;
// erase m_produce_message_cb
if (m_produce_message_cb_map.find(seq) != m_produce_message_cb_map.end())
m_produce_message_cb_map.erase(seq);
}
while (!m_callbacks.empty() && m_callbacks.front() == nullptr) {
m_callbacks.pop_front();
@@ -1118,6 +1491,45 @@ void PrinterFileSystem::RecvMessageThread()
break;
}
}
if (m_messages.empty() && !m_produce_message_cb_map.empty()) {
auto it = m_produce_message_cb_map.begin();
while(it != m_produce_message_cb_map.end()) {
std::string msg;
auto prodeuce_message_cb = it->second;
l.unlock();
int res = prodeuce_message_cb(msg);
l.lock();
if (res == CONTINUE || res == SUCCESS) {
m_messages.emplace_back(msg);
if (res == SUCCESS) {
it = m_produce_message_cb_map.erase(it);
continue;
}
it++;
} else {
int seq2 = it->first - m_sequence;
// erase it
it = m_produce_message_cb_map.erase(it);
if (size_t(seq2) >= m_callbacks.size())
continue;
auto c = m_callbacks[seq2];
if (c == nullptr)
continue;;
m_callbacks[seq2] = nullptr;
if (seq2 == 0) {
// if produce message return error, erase callback and sequence should plus
while (!m_callbacks.empty() && m_callbacks.front() == nullptr) {
m_callbacks.pop_front();
++m_sequence;
}
}
l.unlock();
c(res, json(), nullptr);
l.lock();
}
}
}
if (!m_messages.empty()) {
auto & msg = m_messages.front();
// OutputDebugStringA(msg.c_str());
@@ -1201,7 +1613,7 @@ void PrinterFileSystem::HandleResponse(boost::unique_lock<boost::mutex> &l, Bamb
int result2 = c(result, resp, json_end);
l.lock();
if (result2 != CONTINUE) {
int seq2 = seq - m_sequence;
int seq2 = seq - m_sequence;
m_callbacks[seq2] = callback_t2();
if (seq2 == 0) {
while (!m_callbacks.empty() && m_callbacks.front() == nullptr) {
@@ -1214,6 +1626,11 @@ void PrinterFileSystem::HandleResponse(boost::unique_lock<boost::mutex> &l, Bamb
CancelRequest(seq);
l.lock();
}
// error should erase m_produce_message_cb
if (m_produce_message_cb_map.find(seq2) != m_produce_message_cb_map.end()) {
m_produce_message_cb_map.erase(seq2);
}
}
}
}
@@ -1232,7 +1649,7 @@ void PrinterFileSystem::Reconnect(boost::unique_lock<boost::mutex> &l, int resul
if (m_session.owner == nullptr)
return;
json r;
while (!m_callbacks.empty()) {
while(!m_callbacks.empty()) {
auto c = m_callbacks.front();
m_callbacks.pop_front();
++m_sequence;
@@ -1245,7 +1662,9 @@ void PrinterFileSystem::Reconnect(boost::unique_lock<boost::mutex> &l, int resul
while (m_stopped) {
if (m_session.owner == nullptr)
return;
m_cond.wait(l);
m_status = Status::Reconnecting;
SendChangedEvent(EVT_STATUS_CHANGED, m_status);
m_cond.wait(l);
}
wxLogMessage("PrinterFileSystem::Reconnect Initializing");
m_status = Status::Initializing;
@@ -1271,6 +1690,7 @@ void PrinterFileSystem::Reconnect(boost::unique_lock<boost::mutex> &l, int resul
Bambu_Tunnel tunnel = nullptr;
int ret = Bambu_Create(&tunnel, url.c_str());
if (ret == 0) {
Bambu_SetLogger(tunnel, DumpLog, this);
ret = Bambu_Open(tunnel);
}
@@ -1297,15 +1717,19 @@ void PrinterFileSystem::Reconnect(boost::unique_lock<boost::mutex> &l, int resul
}
wxLogMessage("PrinterFileSystem::Reconnect Failed");
m_status = Status::Failed;
SendChangedEvent(EVT_STATUS_CHANGED, m_status, "", url.size() < 2 ? 1 : m_last_error);
m_cond.timed_wait(l, boost::posix_time::seconds(10));
}
m_status = Status::ListSyncing;
SendChangedEvent(EVT_STATUS_CHANGED, m_status);
#ifdef PRINTER_FILE_SYSTEM_TEST
PostCallback([this] { SendChangedEvent(EVT_FILE_CHANGED); });
#else
PostCallback([this] { m_task_flags = 0; ListAllFiles(); });
PostCallback([this] {
m_task_flags = 0;
m_status = Status::ListSyncing;
SendChangedEvent(EVT_STATUS_CHANGED, m_status);
});
#endif
}

View File

@@ -22,39 +22,53 @@ wxDECLARE_EVENT(EVT_FILE_CHANGED, wxCommandEvent);
wxDECLARE_EVENT(EVT_SELECT_CHANGED, wxCommandEvent);
wxDECLARE_EVENT(EVT_THUMBNAIL, wxCommandEvent);
wxDECLARE_EVENT(EVT_DOWNLOAD, wxCommandEvent);
wxDECLARE_EVENT(EVT_RAMDOWNLOAD, wxCommandEvent);
wxDECLARE_EVENT(EVT_MEDIA_ABILITY_CHANGED, wxCommandEvent);
wxDECLARE_EVENT(EVT_UPLOADING, wxCommandEvent);
wxDECLARE_EVENT(EVT_UPLOAD_CHANGED, wxCommandEvent);
class PrinterFileSystem : public wxEvtHandler, public boost::enable_shared_from_this<PrinterFileSystem>, BambuLib
{
static const int CTRL_TYPE = 0x3001;
enum {
LIST_INFO = 0x0001,
SUB_FILE = 0x0002,
FILE_DEL = 0x0003,
FILE_DOWNLOAD = 0X0004,
NOTIFY_FIRST = 0x0100,
LIST_CHANGE_NOTIFY = 0x0100,
LIST_RESYNC_NOTIFY = 0x0101,
TASK_CANCEL = 0x1000
LIST_INFO = 0x0001,
SUB_FILE = 0x0002,
FILE_DEL = 0x0003,
FILE_DOWNLOAD = 0x0004,
FILE_UPLOAD = 0x0005,
REQUEST_MEDIA_ABILITY = 0x0007,
NOTIFY_FIRST = 0x0100,
LIST_CHANGE_NOTIFY = 0x0100,
LIST_RESYNC_NOTIFY = 0x0101,
TASK_CANCEL = 0x1000
};
public:
enum {
SUCCESS = 0,
CONTINUE = 1,
ERROR_JSON = 2,
ERROR_PIPE = 3,
ERROR_CANCEL = 4,
ERROR_RES_BUSY = 5,
FILE_NO_EXIST = 10,
FILE_NAME_INVALID = 11,
FILE_SIZE_ERR = 12,
FILE_OPEN_ERR = 13,
FILE_READ_WRITE_ERR = 14,
FILE_CHECK_ERR = 15,
FILE_TYPE_ERR = 16,
STORAGE_UNAVAILABLE = 17,
SUCCESS = 0,
CONTINUE = 1,
ERROR_JSON = 2,
ERROR_PIPE = 3,
ERROR_CANCEL = 4,
ERROR_RES_BUSY = 5,
ERROR_TIME_OUT = 6,
FILE_NO_EXIST = 10,
FILE_NAME_INVALID = 11,
FILE_SIZE_ERR = 12,
FILE_OPEN_ERR = 13,
FILE_READ_WRITE_ERR = 14,
FILE_CHECK_ERR = 15,
FILE_TYPE_ERR = 16,
STORAGE_UNAVAILABLE = 17,
API_VERSION_UNSUPPORT = 18,
FILE_EXIST = 19,
STORAGE_SPACE_NOT_ENOUGH = 20,
FILE_CREATE_ERR = 21,
FILE_WRITE_ERR = 22,
MD5_COMPARE_ERR = 23,
FILE_RENAME_ERR = 24,
SEND_ERR = 25,
};
@@ -90,12 +104,28 @@ public:
template<typename T> using Callback = std::function<void(int, T)>;
enum Flags {
FF_SELECT = 1,
FF_THUMNAIL = 2, // Thumbnail ready
FF_DOWNLOAD = 4, // Request download
FF_DELETED = 8, // Request delete
FF_FETCH_MODEL = 16,// Request model
FF_THUMNAIL_RETRY = 0x100, // Thumbnail need retry
FF_SELECT = 1,
FF_THUMNAIL = 2, // Thumbnail ready
FF_DOWNLOAD = 4, // Request download
FF_DELETED = 8, // Request delete
FF_FETCH_MODEL = 16, // Request model
FF_UPLOADING = 1 << 5, // File uploading
FF_UPLOADDONE = 1 << 6, // File upload done
FF_UPLOADCANCEL = 1 << 7, // File upload cancel
FF_THUMNAIL_RETRY = 0x100, // Thumbnail need retry
};
enum UploadStatus
{
Uploading = 1 << 0,
UploadDone = 1 << 1,
UploadCancel = 1 << 2,
};
enum RequestMediaAbilityStatus
{
S_SUCCESS,
S_FAILED
};
struct Progress
@@ -106,6 +136,7 @@ public:
};
struct Download;
struct Upload;
struct File
{
@@ -128,9 +159,23 @@ public:
friend bool operator<(File const & l, File const & r) { return l.time > r.time; }
};
struct UploadFile
{
std::string name;
std::string path;
std::string select_storage;
int flags{0};
boost::uint32_t size{0};
boost::uint32_t chunk_size{0}; // KB
std::unique_ptr<Upload> upload;
bool IsUploading() const { return flags & FF_UPLOADING; }
};
struct Void {};
typedef std::vector<File> FileList;
typedef std::vector<std::string> MediaAbilityList;
void ListAllFiles();
@@ -138,6 +183,17 @@ public:
void DownloadFiles(size_t index, std::string const &path);
void GetPickImage(int id, const std::string &local_path, const std::string &path);
void GetPickImages(const std::vector<std::string> &local_paths, const std::vector<std::string> &targetpaths);
void DownloadRamFile(int index, const std::string &local_path, const std::string &param);
void SendExistedFile();
void SendConnectFail();
void DownloadCheckFiles(std::string const &path);
bool DownloadCheckFile(size_t index);
@@ -170,6 +226,7 @@ public:
ListSyncing,
ListReady,
Failed,
Reconnecting,
};
Status GetStatus() const { return m_status; }
@@ -185,6 +242,16 @@ public:
void Stop(bool quit = false);
boost::uint32_t RequestMediaAbility(int api_version);
void RequestUploadFile();
MediaAbilityList GetMediaAbilityList() const;
void SetUploadFile(const std::string& path, const std::string& name, const std::string& select_storage);
void CancelUploadTask(bool send_cancel_req = true);
private:
void BuildGroups();
@@ -217,13 +284,14 @@ private:
typedef std::function<int(int, json const &resp, unsigned char const *data)> callback_t2;
template <typename T>
boost::uint32_t SendRequest(int type, json const& req, Translator<T> const& translator, Callback<T> const& callback)
typedef std::function<int(std::string &msg)> callback_t3;
template<typename T> boost::uint32_t SendRequest(int type, json const &req, Translator<T> const &translator, Callback<T> const &callback, const std::string &param = "")
{
auto c = [translator, callback, this](int result, json const &resp, unsigned char const *data) -> int
{
T t;
if (result == 0 || result == CONTINUE) {
if (result == 0 || result == CONTINUE || result == FILE_EXIST) {
try {
int n = (translator != nullptr) ? translator(resp, t, data) : 0;
result = n == 0 ? result : n;
@@ -235,7 +303,7 @@ private:
PostCallback<T>(callback, result, t);
return result;
};
return SendRequest(type, req, c);
return SendRequest(type, req, c, param);
}
template<typename T> using Applier = std::function<void(T const &)>;
@@ -265,7 +333,7 @@ private:
InstallNotify(type, c);
}
boost::uint32_t SendRequest(int type, json const &req, callback_t2 const &callback);
boost::uint32_t SendRequest(int type, json const &req, callback_t2 const &callback, const std::string &param = "");
void InstallNotify(int type, callback_t2 const &callback);
@@ -289,6 +357,8 @@ private:
void PostCallback(std::function<void(void)> const & callback);
int UploadFileTask(std::shared_ptr<UploadFile> upload_file, boost::uint64_t seq, std::string &msg);
protected:
FileType m_file_type = F_INVALID_TYPE;
std::string m_file_storage;
@@ -298,6 +368,7 @@ protected:
std::vector<size_t> m_group_year;
std::vector<size_t> m_group_month;
std::vector<int> m_group_flags;
std::shared_ptr<UploadFile> m_upload_file;
private:
size_t m_select_count = 0;
@@ -305,6 +376,8 @@ private:
size_t m_lock_end = 0;
int m_task_flags = 0;
std::vector<bool> m_download_states;
private:
struct Session
{
@@ -315,6 +388,7 @@ private:
boost::uint32_t m_sequence = 0;
boost::uint32_t m_download_seq = 0;
boost::uint32_t m_fetch_model_seq = 0;
boost::uint32_t m_upload_seq = 0;
std::deque<std::string> m_messages;
std::deque<callback_t2> m_callbacks;
std::deque<callback_t2> m_notifies;
@@ -324,6 +398,9 @@ private:
boost::thread m_recv_thread;
Status m_status;
int m_last_error = 0;
MediaAbilityList m_media_ability_list;
std::map<boost::uint32_t, callback_t3> m_produce_message_cb_map;
};
#endif // !slic3r_GUI_PrinterFileSystem_h_

View File

@@ -88,7 +88,9 @@ enum PrintDialogStatus {
PrintStatusNotSupportedPrintAll,
PrintStatusBlankPlate,
PrintStatusUnsupportedPrinter,
PrintStatusTimelapseWarning
PrintStatusTimelapseWarning,
PrintStatusPublicInitFailed,
PrintStatusPublicUploadFiled
};
class Material

View File

@@ -29,12 +29,13 @@ namespace GUI {
#define LIST_REFRESH_INTERVAL 200
#define MACHINE_LIST_REFRESH_INTERVAL 2000
constexpr int timeout_period = 15000; // ms
wxDEFINE_EVENT(EVT_UPDATE_USER_MACHINE_LIST, wxCommandEvent);
wxDEFINE_EVENT(EVT_PRINT_JOB_CANCEL, wxCommandEvent);
wxDEFINE_EVENT(EVT_SEND_JOB_SUCCESS, wxCommandEvent);
wxDEFINE_EVENT(EVT_CLEAR_IPADDRESS, wxCommandEvent);
void SendToPrinterDialog::stripWhiteSpace(std::string& str)
{
if (str == "") { return; }
@@ -260,6 +261,14 @@ SendToPrinterDialog::SendToPrinterDialog(Plater *plater)
m_button_refresh->Bind(wxEVT_BUTTON, &SendToPrinterDialog::on_refresh, this);
m_sizer_printer->Add(m_button_refresh, 0, wxALL | wxLEFT, FromDIP(5));
/*select storage*/
m_storage_panel = new wxPanel(this);
m_storage_panel->SetBackgroundColour(*wxWHITE);
m_storage_sizer = new wxBoxSizer(wxHORIZONTAL);
m_storage_panel->SetSizer(m_storage_sizer);
m_storage_panel->Layout();
m_statictext_printer_msg = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL);
m_statictext_printer_msg->SetFont(::Label::Body_13);
m_statictext_printer_msg->SetForegroundColour(*wxBLACK);
@@ -517,6 +526,7 @@ SendToPrinterDialog::SendToPrinterDialog(Plater *plater)
m_sizer_main->Add(m_line_materia, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30));
m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(12));
m_sizer_main->Add(m_sizer_printer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30));
m_sizer_main->Add(m_storage_panel, 0, wxALIGN_CENTER|wxTOP, FromDIP(8));
m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(11));
m_sizer_main->Add(m_statictext_printer_msg, 0, wxALIGN_CENTER_HORIZONTAL, 0);
m_sizer_main->Add(0, 1, 0, wxTOP, FromDIP(22));
@@ -538,6 +548,49 @@ SendToPrinterDialog::SendToPrinterDialog(Plater *plater)
wxGetApp().UpdateDlgDarkUI(this);
}
std::string SendToPrinterDialog::get_storage_selected()
{
for (const auto& radio : m_storage_radioBox) {
if (radio->GetValue()) {
return radio->GetLabel().ToStdString();
}
}
return "";
}
void SendToPrinterDialog::update_storage_list(std::vector<std::string> storages)
{
m_storage_radioBox.clear();
m_storage_panel->DestroyChildren();
for (int i=0; i < storages.size(); i++) {
RadioBox* radiobox = new RadioBox(m_storage_panel);
Label* storage_text = new Label(m_storage_panel, storages[i]);
radiobox->SetLabel(storages[i]);
radiobox->Bind(wxEVT_LEFT_DOWN, [this, radiobox](auto& e) {
for (const auto& radio : m_storage_radioBox) {
radio->SetValue(false);
}
radiobox->SetValue(true);
});
if (m_storage_radioBox.size() > 0) {
m_storage_sizer->Add(0, 0, 0, wxEXPAND|wxLEFT, FromDIP(6));
auto radio = m_storage_radioBox.front();
radio->SetValue(true);
}
m_storage_sizer->Add(radiobox, 0, wxALIGN_CENTER, 0);
m_storage_sizer->Add(0, 0, 0, wxEXPAND|wxLEFT, FromDIP(6));
m_storage_sizer->Add(storage_text, 0, wxALIGN_CENTER,0);
m_storage_radioBox.push_back(radiobox);
}
m_storage_panel->Layout();
m_storage_panel->Fit();
Layout();
Fit();
}
void SendToPrinterDialog::update_print_error_info(int code, std::string msg, std::string extra)
{
m_print_error_code = code;
@@ -669,6 +722,15 @@ void SendToPrinterDialog::init_timer()
void SendToPrinterDialog::on_cancel(wxCloseEvent &event)
{
m_worker->cancel_all();
if (m_file_sys) {
m_file_sys->CancelUploadTask();
if (m_task_timer && m_task_timer->IsRunning()) {
m_task_timer->Stop();
m_task_timer.reset();
}
}
this->EndModal(wxID_CANCEL);
}
@@ -706,6 +768,9 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event)
m_status_bar->set_cancel_callback_fina([this]() {
BOOST_LOG_TRIVIAL(info) << "print_job: enter canceled";
m_worker->cancel_all();
if (m_file_sys) {
m_file_sys->CancelUploadTask();
}
m_is_canceled = true;
wxCommandEvent* event = new wxCommandEvent(EVT_PRINT_JOB_CANCEL);
wxQueueEvent(this, event);
@@ -760,42 +825,70 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event)
fs::path default_output_file_path = boost::filesystem::path(default_output_file.c_str());
file_name = default_output_file_path.filename().string();
}*/
if (obj_->is_lan_mode_printer()) {
update_print_status_msg(wxEmptyString, false, false);
if (m_file_sys) {
PrintPrepareData print_data;
m_plater->get_print_job_data(&print_data);
std::string project_name = m_current_project_name.utf8_string() + ".3mf";
std::string _3mf_path = print_data._3mf_path.string();
m_file_sys->SetUploadFile(_3mf_path, project_name, get_storage_selected());
m_file_sys->RequestUploadFile();
// time out
if (m_task_timer && m_task_timer->IsRunning())
m_task_timer->Stop();
m_task_timer.reset(new wxTimer());
m_task_timer->SetOwner(this);
this->Bind(
wxEVT_TIMER,
[this, wfs = boost::weak_ptr(m_file_sys)](auto e) {
show_status(PrintDialogStatus::PrintStatusPublicUploadFiled);
boost::shared_ptr fs(wfs.lock());
if (!fs) return;
fs->CancelUploadTask(false);
update_print_status_msg(_L("Upload file timeout, please check if the firmware version supports it."), false, true);
},
m_task_timer->GetId());
m_task_timer->StartOnce(timeout_period);
}
} else {
auto m_send_job = std::make_unique<SendJob>(m_printer_last_select);
m_send_job->m_dev_ip = obj_->dev_ip;
m_send_job->m_access_code = obj_->get_access_code();
m_send_job->m_dev_ip = obj_->dev_ip;
m_send_job->m_access_code = obj_->get_access_code();
#if !BBL_RELEASE_TO_PUBLIC
m_send_job->m_local_use_ssl_for_ftp = wxGetApp().app_config->get("enable_ssl_for_ftp") == "true" ? true : false;
m_send_job->m_local_use_ssl_for_mqtt = wxGetApp().app_config->get("enable_ssl_for_mqtt") == "true" ? true : false;
m_send_job->m_local_use_ssl_for_ftp = wxGetApp().app_config->get("enable_ssl_for_ftp") == "true" ? true : false;
m_send_job->m_local_use_ssl_for_mqtt = wxGetApp().app_config->get("enable_ssl_for_mqtt") == "true" ? true : false;
#else
m_send_job->m_local_use_ssl_for_ftp = obj_->local_use_ssl_for_ftp;
m_send_job->m_local_use_ssl_for_mqtt = obj_->local_use_ssl_for_mqtt;
m_send_job->m_local_use_ssl_for_ftp = obj_->local_use_ssl_for_ftp;
m_send_job->m_local_use_ssl_for_mqtt = obj_->local_use_ssl_for_mqtt;
#endif
m_send_job->connection_type = obj_->connection_type();
m_send_job->cloud_print_only = true;
m_send_job->has_sdcard = obj_->get_sdcard_state() == MachineObject::SdcardState::HAS_SDCARD_NORMAL;
m_send_job->set_project_name(m_current_project_name.utf8_string());
m_send_job->connection_type = obj_->connection_type();
m_send_job->cloud_print_only = true;
m_send_job->has_sdcard = obj_->get_sdcard_state() == MachineObject::SdcardState::HAS_SDCARD_NORMAL;
m_send_job->set_project_name(m_current_project_name.utf8_string());
enable_prepare_mode = false;
enable_prepare_mode = false;
m_send_job->on_check_ip_address_fail([this](int result) {
wxCommandEvent* evt = new wxCommandEvent(EVT_CLEAR_IPADDRESS);
wxQueueEvent(this, evt);
wxGetApp().show_ip_address_enter_dialog();
});
m_send_job->on_check_ip_address_fail([this](int result) {
wxCommandEvent *evt = new wxCommandEvent(EVT_CLEAR_IPADDRESS);
wxQueueEvent(this, evt);
wxGetApp().show_ip_address_enter_dialog();
});
if (obj_->is_lan_mode_printer()) {
m_send_job->set_check_mode();
m_send_job->check_and_continue();
if (obj_->is_lan_mode_printer()) {
m_send_job->set_check_mode();
m_send_job->check_and_continue();
}
replace_job(*m_worker, std::move(m_send_job));
}
replace_job(*m_worker, std::move(m_send_job));
BOOST_LOG_TRIVIAL(info) << "send_job: send print job";
}
@@ -837,9 +930,10 @@ void SendToPrinterDialog::update_user_machine_list()
void SendToPrinterDialog::on_refresh(wxCommandEvent &event)
{
BOOST_LOG_TRIVIAL(info) << "m_printer_last_select: on_refresh";
show_status(PrintDialogStatus::PrintStatusRefreshingMachineList);
update_user_machine_list();
/* show_status(PrintDialogStatus::PrintStatusRefreshingMachineList);
update_user_machine_list();*/
/*todo refresh*/
if (m_file_sys) { m_file_sys->Retry(); }
}
void SendToPrinterDialog::on_print_job_cancel(wxCommandEvent &evt)
@@ -991,8 +1085,11 @@ void SendToPrinterDialog::on_selection_changed(wxCommandEvent &event)
obj->command_request_push_all();
if (!dev->get_selected_machine()) {
dev->set_selected_machine(m_printer_last_select, true);
if (m_file_sys) m_file_sys.reset();
}else if (dev->get_selected_machine()->dev_id != m_printer_last_select) {
update_storage_list(std::vector<std::string>());
dev->set_selected_machine(m_printer_last_select, true);
if (m_file_sys) m_file_sys.reset();
}
}
else {
@@ -1076,8 +1173,137 @@ void SendToPrinterDialog::update_show_status()
}
if (!m_is_in_sending_mode) {
show_status(PrintDialogStatus::PrintStatusReadingFinished);
return;
if (obj_->connection_type() == "lan") {
show_status(PrintDialogStatus::PrintStatusReadingFinished);
return;
} else if (obj_->connection_type() == "cloud") {
Enable(obj_ && obj_->is_connected() && obj_->m_push_count > 0);
std::string dev_id = obj_->dev_ip;
if (m_file_sys) {
if (dev_id == m_device_select) {
if ((m_waiting_enable && IsEnabled()) || (m_waiting_support && obj_->file_remote))
m_file_sys->Retry();
return;
}
}
m_device_select.swap(dev_id);
m_file_sys = boost::make_shared<PrinterFileSystem>();
m_file_sys->Attached();
m_file_sys->Bind(EVT_STATUS_CHANGED, [this, wfs = boost::weak_ptr(m_file_sys)](auto e) {
e.Skip();
boost::shared_ptr fs(wfs.lock());
if (!fs) return;
wxString msg;
int status = e.GetInt();
int extra = e.GetExtraLong();
switch (status) {
case PrinterFileSystem::Initializing:
case PrinterFileSystem::Connecting: show_status(PrintDialogStatus::PrintStatusReading); break;
case PrinterFileSystem::ListSyncing: {
show_status(PrintDialogStatus::PrintStatusReading);
boost::uint32_t seq = fs->RequestMediaAbility(3);
if (m_task_timer && m_task_timer->IsRunning())
m_task_timer->Stop();
m_task_timer.reset(new wxTimer());
m_task_timer->SetOwner(this);
this->Bind(wxEVT_TIMER, [this, wfs_1 = boost::weak_ptr(fs), seq](auto e) {
show_status(PrintDialogStatus::PrintStatusPublicUploadFiled);
boost::shared_ptr fs_1(wfs_1.lock());
if (!fs_1) return;
fs_1->CancelUploadTask(false);
update_print_status_msg(_L("Media capability acquisition timeout, please check if the firmware version supports it."), false, true);
}, m_task_timer->GetId());
m_task_timer->StartOnce(timeout_period);
break;
}
case PrinterFileSystem::Failed: msg = _L("Please check the network and try again, You can restart or update the printer if the issue persists."); break;
}
if (!msg.empty()) {
show_status(PrintDialogStatus::PrintStatusPublicInitFailed);
update_print_status_msg(msg, false, true);
}
if (e.GetInt() == PrinterFileSystem::Initializing) {
CallAfter([=] {
boost::shared_ptr fs(wfs.lock());
if (!fs) return;
fetchUrl(boost::weak_ptr(fs));
});
}
});
m_file_sys->Bind(EVT_MEDIA_ABILITY_CHANGED, [this, wfs = boost::weak_ptr(m_file_sys)](auto e) {
boost::shared_ptr fs(wfs.lock());
if (!fs) return;
if (m_task_timer && m_task_timer->IsRunning()) {
m_task_timer->Stop();
m_task_timer.reset();
}
m_ability_list = fs->GetMediaAbilityList();
if (e.GetInt() == PrinterFileSystem::RequestMediaAbilityStatus::S_SUCCESS) {
update_storage_list(m_ability_list);
show_status(PrintDialogStatus::PrintStatusReadingFinished);
} else {
show_status(PrintDialogStatus::PrintStatusPublicInitFailed);
update_print_status_msg(e.GetString(), false, true);
}
});
m_file_sys->Bind(EVT_UPLOADING, [this, wfs = boost::weak_ptr(m_file_sys)](auto e) {
boost::shared_ptr fs(wfs.lock());
if (!fs) return;
int progress = e.GetInt();
m_status_bar->set_progress(10 + std::floor(progress * 0.9));
if (m_task_timer && m_task_timer->IsRunning()) m_task_timer->Stop();
if (progress == 99) {
m_task_timer.reset(new wxTimer());
m_task_timer->SetOwner(this);
this->Bind(
wxEVT_TIMER,
[this, wfs = boost::weak_ptr(m_file_sys)](auto e) {
show_status(PrintDialogStatus::PrintStatusPublicUploadFiled);
boost::shared_ptr fs(wfs.lock());
if (!fs) return;
fs->CancelUploadTask(false);
update_print_status_msg(_L("Upload file timeout, please check if the firmware version supports it."), false, true);
},
m_task_timer->GetId());
m_task_timer->StartOnce(timeout_period);
}
});
m_file_sys->Bind(EVT_UPLOAD_CHANGED, [this, wfs = boost::weak_ptr(m_file_sys)](auto e) {
boost::shared_ptr fs(wfs.lock());
if (!fs) return;
if (e.GetInt() == PrinterFileSystem::FF_UPLOADDONE) {
show_status(PrintDialogStatus::PrintStatusReadingFinished);
wxCommandEvent *evt = new wxCommandEvent(m_plater->get_send_finished_event());
evt->SetString(from_u8(m_current_project_name.utf8_string()));
wxQueueEvent(m_plater, evt);
} else if (PrinterFileSystem::FF_UPLOADCANCEL) {
show_status(PrintDialogStatus::PrintStatusPublicUploadFiled);
wxString err_msg = e.GetString();
if (err_msg.IsEmpty())
err_msg = _u8L("Sending failed, please try again!");
update_print_status_msg(err_msg, false, true);
}
});
m_file_sys->Start();
}
}
}
@@ -1139,7 +1365,7 @@ void SendToPrinterDialog::show_status(PrintDialogStatus status, std::vector<wxSt
if (status == PrintDialogStatus::PrintStatusInit) {
update_print_status_msg(wxEmptyString, false, false);
Enable_Send_Button(false);
Enable_Refresh_Button(true);
Enable_Refresh_Button(false);
}
else if (status == PrintDialogStatus::PrintStatusNoUserLogin) {
wxString msg_text = _L("No login account, only printers in LAN mode are displayed");
@@ -1217,6 +1443,13 @@ void SendToPrinterDialog::show_status(PrintDialogStatus status, std::vector<wxSt
update_print_status_msg(msg_text, true, true);
Enable_Send_Button(false);
Enable_Refresh_Button(true);
} else if (status == PrintDialogStatus::PrintStatusPublicInitFailed) {
Enable_Send_Button(false);
Enable_Refresh_Button(true);
} else if (status == PrintDialogStatus::PrintStatusPublicUploadFiled) {
prepare_mode();
Enable_Send_Button(true);
Enable_Refresh_Button(true);
}
else {
Enable_Send_Button(true);
@@ -1369,6 +1602,7 @@ bool SendToPrinterDialog::Show(bool show)
// set default value when show this dialog
if (show) {
update_storage_list(std::vector<std::string>());
wxGetApp().reset_to_active();
set_default();
update_user_machine_list();
@@ -1383,12 +1617,84 @@ bool SendToPrinterDialog::Show(bool show)
Layout();
Fit();
if (show) { CenterOnParent(); }
if (m_file_sys) {
show ? m_file_sys->Start() : m_file_sys->Stop();
}
return DPIDialog::Show(show);
}
extern wxString hide_passwd(wxString url, std::vector<wxString> const &passwords);
extern void refresh_agora_url(char const *device, char const *dev_ver, char const *channel, void *context, void (*callback)(void *context, char const *url));
void SendToPrinterDialog::fetchUrl(boost::weak_ptr<PrinterFileSystem> wfs)
{
boost::shared_ptr fs(wfs.lock());
if (!fs) return;
if (!IsEnabled()) {
m_waiting_enable = true;
fs->SetUrl("0");
return;
}
m_waiting_enable = false;
DeviceManager *dm = GUI::wxGetApp().getDeviceManager();
MachineObject *obj = dm->get_selected_machine();
std::string dev_ver = obj->get_ota_version();
std::string dev_id = obj->dev_id;
int remote_proto = obj->file_remote;
if (!remote_proto) {
m_waiting_support = true;
fs->SetUrl("0");
return;
}
if (obj->is_camera_busy_off()) {
fs->SetUrl("0");
return;
}
m_waiting_support = false;
NetworkAgent *agent = wxGetApp().getAgent();
std::string agent_version = agent ? agent->get_version() : "";
if (agent) {
std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""};
agent->get_camera_url(obj->dev_id + "|" + dev_ver + "|" + protocols[remote_proto],
[this, wfs, m = dev_id, v = agent->get_version(), dv = dev_ver](std::string url) {
if (boost::algorithm::starts_with(url, "bambu:///")) {
url += "&device=" + m;
url += "&net_ver=" + v;
url += "&dev_ver=" + dv;
url += "&refresh_url=" + boost::lexical_cast<std::string>(&refresh_agora_url);
url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid");
url += "&cli_ver=" + std::string(SLIC3R_VERSION);
}
BOOST_LOG_TRIVIAL(info) << "SendToPrinter::fetchUrl: camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="});
std::cout << "SendToPrinter::fetchUrl: camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="});
CallAfter([=] {
boost::shared_ptr fs(wfs.lock());
if (!fs) return;
if (boost::algorithm::starts_with(url, "bambu:///")) {
fs->SetUrl(url);
} else {
fs->SetUrl("3");
}
});
});
}
return;
}
SendToPrinterDialog::~SendToPrinterDialog()
{
delete m_refresh_timer;
if (m_task_timer && m_task_timer->IsRunning())
m_task_timer->Stop();
}
}

View File

@@ -38,6 +38,7 @@
#include "Widgets/ScrolledWindow.hpp"
#include <wx/simplebook.h>
#include <wx/hashmap.h>
#include "Printer/PrinterFileSystem.h"
namespace Slic3r {
namespace GUI {
@@ -62,6 +63,7 @@ private:
std::string m_print_error_extra;
std::string m_print_info;
std::string m_printer_last_select;
std::string m_device_select;
wxString m_current_project_name;
TextInput* m_rename_input{ nullptr };
@@ -83,6 +85,8 @@ private:
wxPanel* m_panel_image{ nullptr };
wxPanel* m_rename_normal_panel{ nullptr };
wxPanel* m_line_materia{ nullptr };
wxBoxSizer* m_storage_sizer{ nullptr };
wxPanel* m_storage_panel{ nullptr };
wxSimplebook* m_simplebook{ nullptr };
wxStaticText* m_statictext_finish{ nullptr };
wxStaticText* m_stext_sending{ nullptr };
@@ -111,10 +115,17 @@ private:
wxColour m_colour_def_color{ wxColour(255, 255, 255) };
wxColour m_colour_bold_color{ wxColour(38, 46, 48) };
wxTimer* m_refresh_timer{ nullptr };
std::unique_ptr<wxTimer> m_task_timer{ nullptr };
std::shared_ptr<BBLStatusBarSend> m_status_bar;
std::unique_ptr<Worker> m_worker;
wxScrolledWindow* m_sw_print_failed_info{nullptr};
std::shared_ptr<int> m_token = std::make_shared<int>(0);
std::vector<RadioBox*> m_storage_radioBox;
bool m_waiting_support{ false };
bool m_waiting_enable{ false };
boost::shared_ptr<PrinterFileSystem> m_file_sys;
std::vector<std::string> m_ability_list;
public:
SendToPrinterDialog(Plater* plater = nullptr);
@@ -153,8 +164,13 @@ public:
void show_print_failed_info(bool show, int code = 0, wxString description = wxEmptyString, wxString extra = wxEmptyString);
void update_print_error_info(int code, std::string msg, std::string extra);
void on_change_color_mode() { wxGetApp().UpdateDlgDarkUI(this); }
void update_storage_list(std::vector<std::string> storages);
std::string get_storage_selected();
wxString format_text(wxString& m_msg);
std::vector<std::string> sort_string(std::vector<std::string> strArray);
void fetchUrl(boost::weak_ptr<PrinterFileSystem> wfs);
};
wxDECLARE_EVENT(EVT_CLEAR_IPADDRESS, wxCommandEvent);

View File

@@ -0,0 +1,697 @@
#include <GL/glew.h>
#include "SkipPartCanvas.hpp"
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <boost/log/trivial.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/nowide/fstream.hpp>
#include <expat.h>
#include <earcut/earcut.hpp>
#include <libslic3r/Color.hpp>
#include <filesystem>
#include <map>
wxDEFINE_EVENT(EVT_ZOOM_PERCENT, wxCommandEvent);
wxDEFINE_EVENT(EVT_CANVAS_PART, wxCommandEvent);
namespace Slic3r {
namespace GUI {
SkipPartCanvas::SkipPartCanvas(wxWindow *parent, const wxGLAttributes& dispAttrs)
: wxGLCanvas(parent, dispAttrs) {
context_ = new wxGLContext(this);
this->Bind(wxEVT_PAINT, &SkipPartCanvas::OnPaint, this);
this->Bind(wxEVT_MOUSEWHEEL, &SkipPartCanvas::OnMouseWheel, this);
this->Bind(wxEVT_LEFT_DOWN, &SkipPartCanvas::OnMouseLeftDown, this);
this->Bind(wxEVT_LEFT_DCLICK, &SkipPartCanvas::OnMouseLeftDown, this);
this->Bind(wxEVT_LEFT_UP, &SkipPartCanvas::OnMouseLeftUp, this);
this->Bind(wxEVT_RIGHT_DOWN, &SkipPartCanvas::OnMouseRightDown, this);
this->Bind(wxEVT_RIGHT_UP, &SkipPartCanvas::OnMouseRightUp, this);
this->Bind(wxEVT_SIZE, &SkipPartCanvas::OnSize, this);
this->Bind(wxEVT_MOTION, &SkipPartCanvas::OnMouseMotion, this);
}
void SkipPartCanvas::LoadPickImage(const std::string & path)
{
if(!std::filesystem::exists(path)) return;
auto ParseShapeId = [](cv::Mat image, const std::vector<std::vector<cv::Point>> &contours, const std::vector<cv::Vec4i> &hierarchy, int root_idx) -> uint32_t {
cv::Mat mask = cv::Mat::zeros(image.size(), CV_8UC1);
cv::drawContours(mask, contours, root_idx, 255, cv::FILLED);
int child = hierarchy[root_idx][2];
while (child != -1) {
cv::drawContours(mask, contours, child, 0, cv::FILLED);
child = hierarchy[child][0];
}
std::vector<cv::Vec3b> pixels;
for (int y = 0; y < image.rows; ++y) {
for (int x = 0; x < image.cols; ++x) {
if (mask.at<uchar>(y, x)) { pixels.push_back(image.at<cv::Vec3b>(y, x)); }
}
}
std::map<cv::Vec3b, int, std::function<bool(const cv::Vec3b &, const cv::Vec3b &)>> colorCount(
[](const cv::Vec3b &a, const cv::Vec3b &b) { return std::lexicographical_compare(a.val, a.val + 3, b.val, b.val + 3); });
for (auto &c : pixels) colorCount[c]++;
cv::Vec3b main_color;
int max_count = 0;
int total_count = 0;
for (const auto &kv : colorCount) {
if (kv.second > max_count) {
max_count = kv.second;
main_color = kv.first;
}
total_count += kv.second;
}
SkipIdHelper helper{main_color[2], main_color[1], main_color[0]};
helper.reverse();
return (max_count * 2 > total_count) ? helper.value : 0;
};
parts_state_.clear();
parts_triangles_.clear();
pick_parts_.clear();
int preffered_w{FromDIP(400)}, preffered_h{FromDIP(400)};
cv::Mat src_image = cv::imread(path, cv::IMREAD_UNCHANGED);
cv::cvtColor(src_image, src_image, cv::COLOR_BGRA2BGR); // remove alpha
float zoom_x{static_cast<float>(preffered_w) / src_image.cols};
float zoom_y{static_cast<float>(preffered_h) / src_image.rows};
float image_scale{0};
if (abs(zoom_x - 1) > abs(zoom_y - 1))
image_scale = zoom_x;
else
image_scale = zoom_y;
image_view_scale_ = 1 / image_scale;
pick_image_ = src_image;
std::vector<cv::Mat> channels;
cv::Mat gray; // convert to gray
cv::cvtColor(pick_image_, gray, cv::COLOR_BGR2GRAY);
cv::Mat mask; // convery to binary
cv::threshold(gray, mask, 0, 255, cv::THRESH_BINARY);
std::vector<std::vector<cv::Point>> pick_counters;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(mask, pick_counters, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_TC89_KCOS);
auto compute_depth = [&](int idx) {
int depth = 0;
while (hierarchy[idx][3] != -1) {
depth++;
idx = hierarchy[idx][3];
}
return depth;
};
for (int i = 0; i < pick_counters.size(); ++i) {
int depth = compute_depth(i);
int parent = hierarchy[i][3];
if (parent != -1) continue;
auto id = ParseShapeId(pick_image_, pick_counters, hierarchy, i);
if (id > 0) {
std::vector<FloatPoint> flat_points;
std::vector<std::vector<FloatPoint>> polygon;
// part body
{
polygon.emplace_back();
for (const auto &pt : pick_counters[i]) {
FloatPoint fp{pt.x * 1.0f, pt.y * 1.0f};
polygon.back().push_back(fp);
flat_points.push_back(fp);
}
int child = hierarchy[i][2];
while (child != -1) {
polygon.emplace_back();
for (const auto &pt : pick_counters[child]) {
FloatPoint fp{pt.x * 1.0f, pt.y * 1.0f};
polygon.back().push_back(fp);
flat_points.push_back(fp);
}
child = hierarchy[child][0];
}
std::vector<uint32_t> indices = mapbox::earcut<uint32_t>(polygon);
std::vector<FloatPoint> final_counter;
for (size_t j = 0; j < indices.size(); j += 3) {
final_counter.push_back(flat_points[indices[j]]);
final_counter.push_back(flat_points[indices[j + 1]]);
final_counter.push_back(flat_points[indices[j + 2]]);
}
parts_triangles_[id].emplace_back(final_counter);
}
// part outlines
{
pick_parts_[id].emplace_back(pick_counters[i]);
int child = hierarchy[i][2];
while (child != -1) {
pick_parts_[id].emplace_back(pick_counters[child]);
child = hierarchy[child][0];
}
}
if (parts_state_.find(id) == parts_state_.end()) parts_state_.emplace(id, psUnCheck);
}
}
}
void SkipPartCanvas::ZoomIn(const int zoom_percent)
{
SetZoomPercent(zoom_percent_ + zoom_percent);
Refresh();
}
void SkipPartCanvas::ZoomOut(const int zoom_percent)
{
SetZoomPercent(zoom_percent_ - zoom_percent);
Refresh();
}
void SkipPartCanvas::SwitchDrag(const bool drag_on)
{
fixed_draging_ = drag_on;
AutoSetCursor();
}
void SkipPartCanvas::UpdatePartsInfo(const PartsInfo& parts)
{
for (auto const& part : parts) {
if (auto res = parts_state_.find(part.first); res != parts_state_.end())
res->second = part.second;
}
Refresh();
}
void DrawRoundedRect(float x, float y, float width, float height, float radius, const ColorRGB& color, int segments = 16)
{
glColor3f(color.r(), color.g(), color.b());
// 1. Draw center rectangle
glBegin(GL_QUADS);
glVertex2f(x + radius, y + radius);
glVertex2f(x + width - radius, y + radius);
glVertex2f(x + width - radius, y + height - radius);
glVertex2f(x + radius, y + height - radius);
glEnd();
// 2. Draw side rectangles (excluding corners)
glBegin(GL_QUADS);
// Left
glVertex2f(x, y + radius);
glVertex2f(x + radius, y + radius);
glVertex2f(x + radius, y + height - radius);
glVertex2f(x, y + height - radius);
// Right
glVertex2f(x + width - radius, y + radius);
glVertex2f(x + width, y + radius);
glVertex2f(x + width, y + height - radius);
glVertex2f(x + width - radius, y + height - radius);
// Top
glVertex2f(x + radius, y + height - radius);
glVertex2f(x + width - radius, y + height - radius);
glVertex2f(x + width - radius, y + height);
glVertex2f(x + radius, y + height);
// Bottom
glVertex2f(x + radius, y);
glVertex2f(x + width - radius, y);
glVertex2f(x + width - radius, y + radius);
glVertex2f(x + radius, y + radius);
glEnd();
// 3. Draw corners
auto drawCorner = [&](float cx, float cy, float startAngle) {
glBegin(GL_TRIANGLE_FAN);
glVertex2f(cx, cy);
for (int i = 0; i <= segments; ++i) {
float angle = startAngle + (M_PI * 0.5f) * (float)i / segments;
glVertex2f(cx + cosf(angle) * radius, cy + sinf(angle) * radius);
}
glEnd();
};
drawCorner(x + radius, y + radius, M_PI); // bottom-left
drawCorner(x + width - radius, y + radius, 1.5f * M_PI); // bottom-right
drawCorner(x + width - radius, y + height - radius, 0.0f); // top-right
drawCorner(x + radius, y + height - radius, 0.5f * M_PI); // top-left
}
void SkipPartCanvas::Render()
{
constexpr float border_w = 3.f;
constexpr int uncheckd_stencil =1;
constexpr int checkd_stencil = 2;
constexpr int skipped_stencil = 3;
SetCurrent(*context_);
glPushAttrib(GL_ALL_ATTRIB_BITS);
int w, h;
GetClientSize(&w, &h);
#if defined(__APPLE__)
double scale = GetDPIScaleFactor();
glViewport(0, 0, w * scale, h * scale);
#else
glViewport(0, 0, w, h);
#endif
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
auto view_rect = ViewPtToImagePt(wxPoint(w, h));
glOrtho(offset_.x, view_rect.x, view_rect.y, offset_.y, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClearColor(parent_color_.r(), parent_color_.g(), parent_color_.b(), 1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
float rx = offset_.x;
float ry = offset_.y;
float rw = view_rect.x - offset_.x;
float rh = view_rect.y - offset_.y;
float radius = std::min(rw, rh) * 0.05f;
DrawRoundedRect(rx, ry, rw, rh, radius, ColorRGB{0.9f, 0.9f, 0.9f});
glEnable(GL_BLEND);
glEnable(GL_MULTISAMPLE);
glEnable(GL_LINE_SMOOTH);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glEnable(GL_STENCIL_TEST);
auto draw_shape = [this, border_w](const int stencil, const PartState part_type, const ColorRGB& rgb) {
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glStencilFunc(GL_ALWAYS, stencil, 0xFF);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
for (const auto& contour : parts_triangles_) {
auto part_info = parts_state_.find(contour.first);
if (part_info == parts_state_.end() || part_info->second != part_type)
continue;
glColor3f(1, 1, 1);
for (const auto &contour_item : contour.second) {
glBegin(GL_TRIANGLES);
for (size_t i = 0; i < contour_item.size(); i += 3) {
glVertex2f(contour_item[i][0], contour_item[i][1]);
glVertex2f(contour_item[i + 1][0], contour_item[i + 1][1]);
glVertex2f(contour_item[i + 2][0], contour_item[i + 2][1]);
}
glEnd();
}
}
for (const auto& contour : pick_parts_) {
if (contour.first != this->hover_id_) continue;
auto part_info = parts_state_.find(contour.first);
if (part_info == parts_state_.end() || part_info->second != part_type)
continue;
glColor3f(rgb.r(), rgb.g(), rgb.b());
glLineWidth(border_w);
for (const auto &contour_item : contour.second) {
glBegin(GL_LINE_LOOP);
for (const auto &pt : contour_item) { glVertex2f(pt.x, pt.y); }
glEnd();
}
}
};
// draw unchecked shapes
// stencil1 => unchecked
draw_shape(uncheckd_stencil, psUnCheck, ColorRGB{0, 174 / 255.f, 66 / 255.f});
// draw checked shapes
// stencil2 => checked
draw_shape(checkd_stencil, psChecked, ColorRGB{208 / 255.f, 27 / 255.f, 66 / 255.f});
// draw skipped shapes
// stencil3 => skipped
draw_shape(skipped_stencil, psSkipped, ColorRGB{95 / 255.f, 95 / 255.f, 95 / 255.f});
auto draw_mask = [this, view_rect, border_w, w, h](const int stencil, const PartState part_type,
const ColorRGB& background, const ColorRGB& line, const ColorRGB& bound) {
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glStencilFunc(GL_EQUAL, stencil, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // Don't change stencil
glColor3f(background.r(), background.g(), background.b());
glBegin(GL_POLYGON);
glVertex2f(offset_.x, offset_.y);
glVertex2f(offset_.x, view_rect.y);
glVertex2f(view_rect.x, view_rect.y);
glVertex2f(view_rect.x, offset_.y);
glEnd();
// draw main color
glColor3f(line.r(), line.g(), line.b());
// re-draw shape bound
for (const auto& contour : pick_parts_) {
auto part_info = parts_state_.find(contour.first);
if (part_info == parts_state_.end() || part_info->second != part_type)
continue;
glColor3f(bound.r(), bound.g(), bound.b());
glLineWidth(border_w);
for (const auto &contour_item : contour.second) {
glBegin(GL_LINE_LOOP);
for (const auto &pt : contour_item) { glVertex2f(pt.x, pt.y); }
glEnd();
}
}
};
draw_mask(checkd_stencil, psChecked, ColorRGB{239 / 255.f, 175 / 255.f, 175 / 255.f},
ColorRGB{225 / 255.f, 71 / 255.f, 71 / 255.f}, ColorRGB{208 / 255.f, 27 / 255.f, 27 / 255.f});
draw_mask(skipped_stencil, psSkipped, ColorRGB{159 / 255.f, 159 / 255.f, 159 / 255.f},
ColorRGB{95 / 255.f, 95 / 255.f, 95 / 255.f}, ColorRGB{95 / 255.f, 95 / 255.f, 95 / 255.f});
glDisable(GL_STENCIL_TEST);
glPopAttrib();
glFlush();
}
void SkipPartCanvas::DebugLogLine(std::string str)
{
//if (!log_ctrl)
// return;
//log_ctrl->AppendText(str + "\n");
}
void SkipPartCanvas::SendSelectEvent(int id, PartState state) {
wxCommandEvent evt(EVT_CANVAS_PART);
evt.SetExtraLong(id);
evt.SetInt(static_cast<int>(state));
wxPostEvent(this, evt);
}
void SkipPartCanvas::SendZoomEvent(int zoom_percent) {
wxCommandEvent evt(EVT_ZOOM_PERCENT);
evt.SetInt(zoom_percent_);
wxPostEvent(this, evt);
}
inline double SkipPartCanvas::Zoom() const
{
return zoom_percent_ / 100.0f;
}
inline wxPoint SkipPartCanvas::ViewPtToImagePt(const wxPoint& view_pt) const
{
return wxPoint(view_pt.x * image_view_scale_ / Zoom(), view_pt.y * image_view_scale_ / Zoom()) + offset_;
}
uint32_t SkipPartCanvas::GetIdAtImagePt(const wxPoint& image_pt) const
{
if (image_pt.x >= 0 && image_pt.x < pick_image_.cols
&& image_pt.y >= 0 && image_pt.y < pick_image_.rows) {
// at(row, col)=>at(y, x)
cv::Vec3b bgr = pick_image_.at<cv::Vec3b>(image_pt.y, image_pt.x);
SkipIdHelper helper{bgr[2], bgr[1], bgr[0]};
helper.reverse();
return helper.value;
} else {
return 0;
}
}
inline uint32_t SkipPartCanvas::GetIdAtViewPt(const wxPoint& view_pt) const
{
wxPoint pt_at_image = ViewPtToImagePt(view_pt);
return GetIdAtImagePt(pt_at_image);
}
void SkipPartCanvas::SetZoomPercent(const int value)
{
zoom_percent_ = std::clamp(value, 100, 1000);
std::ostringstream oss;
oss << "zoom to " << zoom_percent_;
DebugLogLine(oss.str());
SendZoomEvent(zoom_percent_);
}
void SkipPartCanvas::SetOffset(const wxPoint& value)
{
int w, h;
GetClientSize(&w, &h);
int max_w = static_cast<int>(w * (1 - 1 / Zoom())) >= 0 ? static_cast<int>(w * (1 - 1 / Zoom())) : 0;
int max_h = static_cast<int>(w * (1 - 1 / Zoom())) >= 0 ? static_cast<int>(h * (1 - 1 / Zoom())) : 0;
offset_.x = std::clamp(value.x, 0, max_w);
offset_.y = std::clamp(value.y, 0, max_h);
}
void SkipPartCanvas::AutoSetCursor()
{
if(is_draging_ || fixed_draging_)
SetCursor(wxCursor(wxCURSOR_HAND));
else
SetCursor(wxCursor(wxCURSOR_NONE));
}
void SkipPartCanvas::StartDrag(const wxPoint& mouse_pt)
{
drag_start_pt_ = mouse_pt;
drag_start_offset_ = offset_;
is_draging_ = true;
AutoSetCursor();
}
void SkipPartCanvas::ProcessDrag(const wxPoint& mouse_pt)
{
wxPoint drag_offset = (mouse_pt - drag_start_pt_) * image_view_scale_;
SetOffset(- wxPoint(drag_offset.x / Zoom(), drag_offset.y / Zoom()) + drag_start_offset_);
Refresh();
}
void SkipPartCanvas::ProcessHover(const wxPoint& mouse_pt)
{
auto id_at_mouse = GetIdAtViewPt(mouse_pt);
int new_hover_id { -1 };
auto part_state = parts_state_.find(id_at_mouse);
if (part_state != parts_state_.end() && part_state->second == psUnCheck) {
new_hover_id = id_at_mouse;
};
if (new_hover_id != this->hover_id_) {
this->hover_id_ = new_hover_id;
Refresh();
}
}
void SkipPartCanvas::EndDrag()
{
is_draging_ = false;
AutoSetCursor();
}
void SkipPartCanvas::OnPaint(wxPaintEvent &event)
{
wxPaintDC dc(this);
if (!IsShown()) return;
SetCurrent(*context_);
Render();
SwapBuffers();
}
void SkipPartCanvas::OnSize(wxSizeEvent& event)
{
event.Skip();
}
void SkipPartCanvas::OnMouseLeftDown(wxMouseEvent& event)
{
DebugLogLine("OnMouseLeftDown");
if (!event.LeftIsDown()) {
event.Skip();
DebugLogLine("skip----OnMouseLeftDown");
return;
}
if (fixed_draging_)
StartDrag(wxPoint(event.GetX(), event.GetY()));
left_down_ = true;
}
void SkipPartCanvas::OnMouseLeftUp(wxMouseEvent& event)
{
DebugLogLine("OnMouseLeftUp");
if (event.LeftIsDown() || !left_down_) {
event.Skip();
DebugLogLine("skip----OnMouseLeftUp");
return;
}
auto id_at_mouse = GetIdAtViewPt(wxPoint(event.GetX(), event.GetY()));
auto part_state = parts_state_.find(id_at_mouse);
if (part_state != parts_state_.end() && part_state->second != psSkipped) {
if (part_state->second == psUnCheck)
part_state = parts_state_.insert_or_assign(part_state->first, psChecked).first;
else
part_state = parts_state_.insert_or_assign(part_state->first, psUnCheck).first;
// if (select_callback_)
// select_callback_(part_state->first, part_state->second);
SendSelectEvent(part_state->first, part_state->second);
}
left_down_ = false;
if (fixed_draging_)
EndDrag();
else {
Refresh();
}
}
void SkipPartCanvas::OnMouseRightDown(wxMouseEvent& event)
{
DebugLogLine("OnMouseRightDown");
if (!event.RightIsDown()) {
event.Skip();
DebugLogLine("skip----OnMouseRightDown");
return;
}
StartDrag(wxPoint(event.GetX(), event.GetY()));
}
void SkipPartCanvas::OnMouseRightUp(wxMouseEvent& event)
{
DebugLogLine("OnMouseRightUp");
if (event.RightIsDown() || !is_draging_) {
event.Skip();
DebugLogLine("skip----OnMouseRightUp");
return;
}
EndDrag();
}
void SkipPartCanvas::OnMouseMotion(wxMouseEvent& event)
{
ProcessHover(wxPoint(event.GetX(), event.GetY()));
if (!event.RightIsDown() && !(event.LeftIsDown() && fixed_draging_)) {
event.Skip();
return;
}
ProcessDrag(wxPoint(event.GetX(), event.GetY()));
}
void SkipPartCanvas::OnMouseWheel(wxMouseEvent& event)
{
wxPoint view_mouse = wxPoint(event.GetX(), event.GetY());
auto pre_image_pos = ViewPtToImagePt(view_mouse);
SetZoomPercent(zoom_percent_ + 10 * (event.GetWheelRotation() / 120.0));
auto now_image_pos = ViewPtToImagePt(view_mouse);
SetOffset(offset_ - (now_image_pos - pre_image_pos));
Refresh();
}
// Base class with error messages management
void _BBS_3MF_Base::add_error(const std::string &error) const
{
boost::unique_lock l(mutex);
m_errors.push_back(error);
}
void _BBS_3MF_Base::clear_errors() { m_errors.clear(); }
void _BBS_3MF_Base::log_errors()
{
for (const std::string &error : m_errors) BOOST_LOG_TRIVIAL(error) << error;
}
ModelSettingHelper::ModelSettingHelper(const std::string &path) : path_(path) {}
bool ModelSettingHelper::Parse()
{
boost::nowide::fstream fs(path_);
if (!fs) {
add_error("Failed to open file\n");
return false;
}
XML_Parser parser = XML_ParserCreate(nullptr);
if (!parser) {
add_error("Unable to create parser");
return false;
}
XML_SetUserData(parser, this);
XML_SetElementHandler(parser, ModelSettingHelper::StartElementHandler, ModelSettingHelper::EndElementHandler);
try {
char buffer[4000];
while (fs.read(buffer, sizeof(buffer)) || fs.gcount() > 0) {
auto ret = XML_Parse(parser, buffer, static_cast<int>(fs.gcount()), fs.eof());
if (ret != XML_STATUS_OK) {
add_error("return value of XML_Parse doesn't match XM_STATUS_OK");
XML_ParserFree(parser);
return false;
}
}
}
catch (std::exception& e) {
add_error(std::string("exception:") + e.what());
XML_ParserFree(parser);
return false;
}
XML_ParserFree(parser);
return true;
}
void XMLCALL ModelSettingHelper::StartElementHandler(void *userData, const XML_Char *name, const XML_Char **atts)
{
ModelSettingHelper *self = static_cast<ModelSettingHelper *>(userData);
if (strcmp(name, "plate") == 0) {
self->context_.current_plate = PlateInfo(); // start a new plate
self->context_.in_plate = true;
} else if (strcmp(name, "metadata") == 0 && self->context_.in_plate) {
std::string key, value;
for (int i = 0; atts[i]; i += 2) {
if (strcmp(atts[i], "key") == 0) key = atts[i + 1];
if (strcmp(atts[i], "value") == 0) value = atts[i + 1];
}
if (key == "index") { self->context_.current_plate.index = std::stoi(value); }
if (key == "label_object_enabled") { self->context_.current_plate.label_object_enabled = value == "true"; }
} else if (strcmp(name, "object") == 0 && self->context_.in_plate) {
ObjectInfo obj;
for (int i = 0; atts[i]; i += 2) {
if (strcmp(atts[i], "identify_id") == 0) obj.identify_id = atoi(atts[i + 1]);
if (strcmp(atts[i], "name") == 0) obj.name = atts[i + 1];
}
self->context_.current_plate.objects.push_back(obj);
}
}
void XMLCALL ModelSettingHelper::EndElementHandler(void *userData, const XML_Char *name)
{
ModelSettingHelper *self = static_cast<ModelSettingHelper *>(userData);
if (strcmp(name, "plate") == 0 && self->context_.in_plate) {
self->context_.plates.push_back(self->context_.current_plate);
self->context_.current_plate = PlateInfo(); // reset
self->context_.in_plate = false;
}
}
std::vector<ObjectInfo> ModelSettingHelper::GetPlateObjects(int plate_idx) {
for (const auto &plate : context_.plates) {
if (plate.index == plate_idx) {
return plate.objects;
}
}
return std::vector<ObjectInfo>();
}
bool ModelSettingHelper::GetLabelObjectEnabled(int plate_idx)
{
for (const auto &plate : context_.plates) {
if (plate.index == plate_idx) { return plate.label_object_enabled; }
}
return false;
}
void ModelSettingHelper::DataHandler(const XML_Char *s, int len)
{
// do nothing
}
}
}

View File

@@ -0,0 +1,169 @@
#ifndef SKIPPARTCANVAS_H
#define SKIPPARTCANVAS_H
#include <wx/wx.h>
#include <wx/glcanvas.h>
#include <opencv2/opencv.hpp>
#include <wx/textctrl.h>
#include <vector>
#include <expat.h>
#include <libslic3r/Color.hpp>
#include <boost/thread/mutex.hpp>
#include "PartSkipCommon.hpp"
wxDECLARE_EVENT(EVT_ZOOM_PERCENT, wxCommandEvent);
wxDECLARE_EVENT(EVT_CANVAS_PART, wxCommandEvent);
namespace Slic3r {
namespace GUI {
using Coord = float;
using FloatPoint = std::array<Coord, 2>;
struct ObjectInfo {
std::string name{""};
int identify_id{-1};
PartState state{psUnCheck};
};
class SkipPartCanvas : public wxGLCanvas
{
union SkipIdHelper
{
uint32_t value = 0;
struct
{
uint8_t b;
uint8_t g;
uint8_t r;
uint8_t _padding;
};
uint8_t bytes[4];
SkipIdHelper() = default;
SkipIdHelper(uint8_t red, uint8_t green, uint8_t blue)
: r(red), g(green), b(blue), _padding(0) {}
SkipIdHelper(uint32_t val): value(val){}
void reverse() {
uint8_t tmp{r};
r = b;
b = tmp;
}
};
public:
SkipPartCanvas(wxWindow *parent, const wxGLAttributes& dispAttrs);
~SkipPartCanvas() = default;
void SetParentBackground(const ColorRGB& color) {
parent_color_ = color;
}
void LoadPickImage(const std::string& path);
void ZoomIn(const int zoom_percent);
void ZoomOut(const int zoom_percent);
void SwitchDrag(const bool drag_on);
void UpdatePartsInfo(const PartsInfo& parts);
void SetZoomPercent(const int value);
void SetOffset(const wxPoint &value);
wxTextCtrl* log_ctrl;
protected:
void OnPaint(wxPaintEvent& event);
void OnSize(wxSizeEvent& event);
void OnMouseLeftDown(wxMouseEvent& event);
void OnMouseLeftUp(wxMouseEvent& event);
void OnMouseRightDown(wxMouseEvent& event);
void OnMouseRightUp(wxMouseEvent& event);
void OnMouseMotion(wxMouseEvent& event);
void OnMouseWheel(wxMouseEvent& event);
private:
wxGLContext* context_;
cv::Mat pick_image_;
std::unordered_map < uint32_t, std::vector<std::vector<FloatPoint>>> parts_triangles_;
std::unordered_map < uint32_t, std::vector<std::vector<cv::Point>>> pick_parts_;
std::unordered_map<uint32_t, PartState> parts_state_;
bool gl_inited_{false};
int zoom_percent_{100};
wxPoint offset_{0,0};
wxPoint drag_start_offset_{0,0};
wxPoint drag_start_pt_{0,0};
bool is_draging_{false};
bool fixed_draging_{false};
bool left_down_{false};
ColorRGB parent_color_ = ColorRGB();
int hover_id_{-1};
double image_view_scale_{1};
void SendSelectEvent(int id, PartState state);
void SendZoomEvent(int zoom_percent);
inline double Zoom() const;
inline wxPoint ViewPtToImagePt(const wxPoint& view_pt) const;
uint32_t GetIdAtImagePt(const wxPoint& image_pt) const;
inline uint32_t GetIdAtViewPt(const wxPoint& view_pt) const;
void ProcessHover(const wxPoint& mouse_pt);
void AutoSetCursor();
void StartDrag(const wxPoint& mouse_pt);
void ProcessDrag(const wxPoint& mouse_pt);
void EndDrag();
void Render();
void DebugLogLine(std::string str);
};
class _BBS_3MF_Base
{
mutable boost::mutex mutex;
mutable std::vector<std::string> m_errors;
protected:
void add_error(const std::string& error) const;
void clear_errors();
public:
void log_errors();
};
struct PlateInfo
{
int index{-1};
std::vector<ObjectInfo> objects;
bool label_object_enabled = false;
};
class ModelSettingHelper : public _BBS_3MF_Base
{
struct ParseContext
{
std::vector<PlateInfo> plates;
PlateInfo current_plate;
ObjectInfo temp_object;
bool in_plate = false;
};
public:
ModelSettingHelper(const std::string &path);
bool Parse();
std::vector<ObjectInfo> GetPlateObjects(int plate_idx);
bool GetLabelObjectEnabled(int plate_idx);
private:
std::string path_;
ParseContext context_;
static void XMLCALL StartElementHandler(void *userData, const XML_Char *name, const XML_Char **atts);
static void XMLCALL EndElementHandler(void *userData, const XML_Char *name);
void DataHandler(const XML_Char *s, int len);
};
}
}
#endif //SKIPPARTCANVAS_H

View File

@@ -233,12 +233,6 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
bSizer_task_name->Add(task_name_panel, 0, wxEXPAND, FromDIP(5));
/* wxFlexGridSizer *fgSizer_task = new wxFlexGridSizer(2, 2, 0, 0);
fgSizer_task->AddGrowableCol(0);
fgSizer_task->SetFlexibleDirection(wxVERTICAL);
fgSizer_task->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);*/
m_printing_stage_value = new wxStaticText(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT | wxST_ELLIPSIZE_END);
m_printing_stage_value->Wrap(-1);
m_printing_stage_value->SetMaxSize(wxSize(FromDIP(800),-1));
@@ -261,24 +255,34 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
m_staticText_profile_value->SetForegroundColour(0x6B6B6B);
auto progress_lr_panel = new wxPanel(parent, wxID_ANY);
progress_lr_panel->SetBackgroundColour(*wxWHITE);
auto m_panel_progress = new wxPanel(parent, wxID_ANY);
m_panel_progress->SetBackgroundColour(*wxWHITE);
auto m_sizer_progressbar = new wxBoxSizer(wxHORIZONTAL);
m_gauge_progress = new ProgressBar(m_panel_progress, wxID_ANY, 100, wxDefaultPosition, wxDefaultSize);
m_gauge_progress = new ProgressBar(progress_lr_panel, wxID_ANY, 100, wxDefaultPosition, wxDefaultSize);
m_gauge_progress->SetValue(0);
m_gauge_progress->SetHeight(PROGRESSBAR_HEIGHT);
m_gauge_progress->SetMaxSize(wxSize(FromDIP(600), -1));
m_panel_progress->SetSizer(m_sizer_progressbar);
m_panel_progress->Layout();
m_panel_progress->SetSize(wxSize(-1, FromDIP(24)));
m_panel_progress->SetMaxSize(wxSize(-1, FromDIP(24)));
wxBoxSizer *bSizer_task_btn = new wxBoxSizer(wxHORIZONTAL);
bSizer_task_btn->Add(FromDIP(10), 0, 0);
m_button_pause_resume = new ScalableButton(m_panel_progress, wxID_ANY, "print_control_pause", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER,true);
StateColor white_bg(std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Disabled), std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Pressed),
std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Hovered), std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Enabled),
std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Normal));
m_button_partskip = new Button(progress_lr_panel, wxEmptyString, "print_control_partskip_disable", 0, 20, wxID_ANY);
m_button_partskip->Enable(false);
m_button_partskip->Hide();
m_button_partskip->SetBackgroundColor(white_bg);
m_button_partskip->SetIcon("print_control_partskip_disable");
m_button_partskip->SetBorderColor(*wxWHITE);
m_button_partskip->SetFont(Label::Body_12);
m_button_partskip->SetCornerRadius(0);
m_button_partskip->SetToolTip(_L("Parts Skip"));
m_button_partskip->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { m_button_partskip->SetIcon("print_control_partskip_hover"); });
m_button_partskip->Bind(wxEVT_LEAVE_WINDOW, [this](auto &e) { m_button_partskip->SetIcon("print_control_partskip"); });
m_button_pause_resume = new ScalableButton(progress_lr_panel, wxID_ANY, "print_control_pause", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER,true);
m_button_pause_resume->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) {
if (m_button_pause_resume->GetToolTipText() == _L("Pause")) {
@@ -301,7 +305,7 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
}
});
m_button_abort = new ScalableButton(m_panel_progress, wxID_ANY, "print_control_stop", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true);
m_button_abort = new ScalableButton(progress_lr_panel, wxID_ANY, "print_control_stop", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true);
m_button_abort->SetToolTip(_L("Stop"));
m_button_abort->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) {
@@ -312,19 +316,14 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
m_button_abort->SetBitmap_("print_control_stop"); }
);
m_sizer_progressbar->Add(m_gauge_progress, 1, wxALIGN_CENTER_VERTICAL, 0);
m_sizer_progressbar->Add(0, 0, 0, wxEXPAND|wxLEFT, FromDIP(18));
m_sizer_progressbar->Add(m_button_pause_resume, 0, wxALL, FromDIP(5));
m_sizer_progressbar->Add(0, 0, 0, wxEXPAND|wxLEFT, FromDIP(18));
m_sizer_progressbar->Add(m_button_abort, 0, wxALL, FromDIP(5));
wxBoxSizer *bSizer_buttons = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer *bSizer_text = new wxBoxSizer(wxHORIZONTAL);
wxPanel* penel_bottons = new wxPanel(parent);
wxPanel* penel_text = new wxPanel(penel_bottons);
wxBoxSizer *bSizer_finish_time = new wxBoxSizer(wxHORIZONTAL);
wxPanel* penel_text = new wxPanel(progress_lr_panel);
wxPanel* penel_finish_time = new wxPanel(progress_lr_panel);
penel_text->SetBackgroundColour(*wxWHITE);
penel_bottons->SetBackgroundColour(*wxWHITE);
penel_finish_time->SetBackgroundColour(*wxWHITE);
wxBoxSizer *sizer_percent = new wxBoxSizer(wxVERTICAL);
sizer_percent->Add(0, 0, 1, wxEXPAND, 0);
@@ -357,55 +356,65 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
m_staticText_progress_left->SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC")));
m_staticText_progress_left->SetForegroundColour(wxColour(146, 146, 146));
// Orca: display the end time of the print
m_staticText_progress_end = new wxStaticText(penel_text, wxID_ANY, L("N/A"), wxDefaultPosition, wxDefaultSize, 0);
m_staticText_progress_end->Wrap(-1);
m_staticText_progress_end->SetFont(
wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC")));
m_staticText_progress_end->SetForegroundColour(wxColour(146, 146, 146));
//fgSizer_task->Add(bSizer_buttons, 0, wxEXPAND, 0);
//fgSizer_task->Add(0, 0, 0, wxEXPAND, FromDIP(5));
wxPanel* panel_button_block = new wxPanel(penel_bottons, wxID_ANY);
panel_button_block->SetMinSize(wxSize(TASK_BUTTON_SIZE.x * 2 + FromDIP(5) * 4, -1));
panel_button_block->SetMinSize(wxSize(TASK_BUTTON_SIZE.x * 2 + FromDIP(5) * 4, -1));
panel_button_block->SetSize(wxSize(TASK_BUTTON_SIZE.x * 2 + FromDIP(5) * 2, -1));
panel_button_block->SetBackgroundColour(*wxWHITE);
m_staticText_layers = new wxStaticText(penel_text, wxID_ANY, _L("Layer: N/A"));
m_staticText_layers->SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC")));
m_staticText_layers->SetForegroundColour(wxColour(146, 146, 146));
m_staticText_layers->Hide();
//bSizer_text->Add(m_staticText_progress_percent, 0, wxALL, 0);
bSizer_text->Add(sizer_percent, 0, wxEXPAND, 0);
bSizer_text->Add(sizer_percent_icon, 0, wxEXPAND, 0);
bSizer_text->Add(0, 0, 1, wxEXPAND, 0);
bSizer_text->Add(m_staticText_layers, 0, wxALIGN_CENTER | wxALL, 0);
bSizer_text->Add(m_staticText_layers, 0, wxALIGN_CENTER_VERTICAL | wxALL, 0);
bSizer_text->Add(0, 0, 0, wxLEFT, FromDIP(20));
bSizer_text->Add(m_staticText_progress_left, 0, wxALIGN_CENTER | wxALL, 0);
// Orca: display the end time of the print
bSizer_text->Add(0, 0, 0, wxLEFT, FromDIP(8));
bSizer_text->Add(m_staticText_progress_end, 0, wxALIGN_CENTER | wxALL, 0);
bSizer_text->Add(m_staticText_progress_left, 0, wxALIGN_CENTER_VERTICAL | wxALL, 0);
penel_text->SetMaxSize(wxSize(FromDIP(600), -1));
// penel_text->SetMaxSize(wxSize(FromDIP(600), -1));
penel_text->SetSizer(bSizer_text);
penel_text->Layout();
bSizer_buttons->Add(penel_text, 1, wxEXPAND | wxALL, 0);
bSizer_buttons->Add(panel_button_block, 0, wxALIGN_CENTER | wxALL, 0);
// Orca: display the end time of the print
m_staticText_progress_end = new wxStaticText(penel_finish_time, wxID_ANY, L("N/A"), wxDefaultPosition, wxDefaultSize, 0);
m_staticText_progress_end->Wrap(-1);
m_staticText_progress_end->SetFont(
wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC")));
m_staticText_progress_end->SetForegroundColour(wxColour(146, 146, 146));
bSizer_finish_time->Add(0, 0, 1, wxEXPAND, 0);
bSizer_finish_time->Add(m_staticText_progress_end, 0, wxLEFT | wxEXPAND, 0);
// penel_finish_time->SetMaxSize(wxSize(FromDIP(600), -1));
penel_finish_time->SetSizer(bSizer_finish_time);
penel_finish_time->Layout();
penel_bottons->SetSizer(bSizer_buttons);
penel_bottons->Layout();
auto progress_lr_sizer = new wxBoxSizer(wxHORIZONTAL);
auto progress_left_sizer = new wxBoxSizer(wxVERTICAL);
auto progress_right_sizer = new wxBoxSizer(wxHORIZONTAL);
progress_left_sizer->Add(penel_text, 0, wxEXPAND | wxALL, 0);
progress_left_sizer->Add(m_gauge_progress, 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(10));
progress_left_sizer->Add(penel_finish_time, 0, wxEXPAND |wxALL, 0);
// progress_left_sizer->SetMaxSize(wxSize(FromDIP(600), -1));
progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18));
progress_right_sizer->Add(m_button_partskip, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0));//5
progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18));
progress_right_sizer->Add(m_button_pause_resume, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0));
progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18));
progress_right_sizer->Add(m_button_abort, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0));
progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18));
progress_lr_sizer->Add(progress_left_sizer, 1, wxEXPAND | wxALL, 0);
progress_lr_sizer->Add(progress_right_sizer, 0, wxEXPAND | wxALL , 0);
progress_lr_panel->SetSizer(progress_lr_sizer);
progress_lr_panel->SetMaxSize(wxSize(FromDIP(720), -1));
progress_lr_panel->Layout();
progress_lr_panel->Fit();
bSizer_subtask_info->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(14));
bSizer_subtask_info->Add(bSizer_task_name, 0, wxEXPAND|wxRIGHT, FromDIP(18));
bSizer_subtask_info->Add(m_staticText_profile_value, 0, wxEXPAND | wxTOP, FromDIP(5));
bSizer_subtask_info->Add(m_printing_stage_value, 0, wxEXPAND | wxTOP, FromDIP(5));
bSizer_subtask_info->Add(penel_bottons, 0, wxEXPAND | wxTOP, FromDIP(10));
bSizer_subtask_info->Add(m_panel_progress, 0, wxEXPAND|wxRIGHT, FromDIP(25));
bSizer_subtask_info->Add(progress_lr_panel, 0, wxEXPAND | wxTOP, FromDIP(5));
m_printing_sizer = new wxBoxSizer(wxHORIZONTAL);
m_printing_sizer->SetMinSize(wxSize(PAGE_MIN_WIDTH, -1));
@@ -413,7 +422,6 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
m_printing_sizer->Add(FromDIP(8), 0, 0, wxEXPAND, 0);
m_printing_sizer->Add(bSizer_subtask_info, 1, wxALL | wxEXPAND, 0);
m_staticline = new wxPanel( parent, wxID_ANY);
m_staticline->SetBackgroundColour(wxColour(238,238,238));
m_staticline->Layout();
@@ -638,6 +646,24 @@ void PrintingTaskPanel::reset_printing_value()
this->set_plate_index(-1);
}
void PrintingTaskPanel::enable_partskip_button(MachineObject* obj, bool enable)
{
int stage = 0;
bool in_calibration_mode = false;
if( obj && (obj->print_type == "system" || CalibUtils::get_calib_mode_by_name(obj->subtask_name, stage) != CalibMode::Calib_None)){
in_calibration_mode = true;
}
if (!enable || in_calibration_mode) {
m_button_partskip->Enable(false);
m_button_partskip->SetLabel("");
m_button_partskip->SetIcon("print_control_partskip_disable");
}else if(obj && obj->is_support_brtc){
m_button_partskip->Enable(true);
m_button_partskip->SetIcon("print_control_partskip");
}
}
void PrintingTaskPanel::enable_pause_resume_button(bool enable, std::string type)
{
if (!enable) {
@@ -1731,6 +1757,7 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co
m_switch_cham_fan->SetValue(false);
/* set default enable state */
m_project_task_panel->enable_partskip_button(nullptr, false);
m_project_task_panel->enable_pause_resume_button(false, "resume_disable");
m_project_task_panel->enable_abort_button(false);
@@ -1751,6 +1778,7 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co
// Connect Events
m_project_task_panel->get_bitmap_thumbnail()->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::refresh_thumbnail_webrequest), NULL, this);
m_project_task_panel->get_partskip_button()->Connect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_subtask_partskip), NULL, this);
m_project_task_panel->get_pause_resume_button()->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_pause_resume), NULL, this);
m_project_task_panel->get_abort_button()->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_abort), NULL, this);
m_project_task_panel->get_market_scoring_button()->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_scoring), NULL, this);
@@ -1810,6 +1838,7 @@ StatusPanel::~StatusPanel()
{
// Disconnect Events
m_project_task_panel->get_bitmap_thumbnail()->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::refresh_thumbnail_webrequest), NULL, this);
m_project_task_panel->get_partskip_button()->Disconnect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_subtask_partskip), NULL, this);
m_project_task_panel->get_pause_resume_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_pause_resume), NULL, this);
m_project_task_panel->get_abort_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_abort), NULL, this);
m_project_task_panel->get_market_scoring_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_scoring), NULL, this);
@@ -1940,6 +1969,35 @@ void StatusPanel::on_market_retry(wxCommandEvent &event)
}
}
void StatusPanel::update_partskip_button(MachineObject *obj) {
if (!obj) return;
auto partskip_button = m_project_task_panel->get_partskip_button();
if( obj->is_support_partskip ){
partskip_button->Show();
}else{
partskip_button->Hide();
}
BOOST_LOG_TRIVIAL(info) << "part skip: is_support_partskip: "<< obj->is_support_partskip;
}
void StatusPanel::on_subtask_partskip(wxCommandEvent &event)
{
if (m_partskip_dlg == nullptr) {
m_partskip_dlg = new PartSkipDialog(this->GetParent());
}
auto dm = GUI::wxGetApp().getDeviceManager();
m_partskip_dlg->InitSchedule(dm->get_selected_machine());
BOOST_LOG_TRIVIAL(info) << "part skip: initial part skip dialog.";
if(m_partskip_dlg->ShowModal() == wxID_OK){
int cnt = m_partskip_dlg->GetAllSkippedPartsNum();
m_project_task_panel->set_part_skipped_count(cnt);
m_project_task_panel->set_part_skipped_dirty(5);
BOOST_LOG_TRIVIAL(info) << "part skip: prepare to filter printer dirty data.";
}
}
void StatusPanel::on_subtask_pause_resume(wxCommandEvent &event)
{
if (obj) {
@@ -3084,14 +3142,18 @@ void StatusPanel::update_subtask(MachineObject *obj)
}
update_model_info();
update_partskip_button(obj);
if (obj->is_system_printing()
|| obj->is_in_calibration()) {
reset_printing_values();
} else if (obj->is_in_printing() || obj->print_status == "FINISH") {
update_partskip_subtask(obj);
if (obj->is_in_prepare() || obj->print_status == "SLICING") {
m_project_task_panel->market_scoring_hide();
m_project_task_panel->get_request_failed_panel()->Hide();
m_project_task_panel->enable_partskip_button(nullptr, false);
m_project_task_panel->enable_abort_button(false);
m_project_task_panel->enable_pause_resume_button(false, "pause_disable");
wxString prepare_text;
@@ -3133,7 +3195,7 @@ void StatusPanel::update_subtask(MachineObject *obj)
} else {
m_project_task_panel->enable_pause_resume_button(true, "pause");
}
m_project_task_panel->enable_partskip_button(obj, true);
// update printing stage
m_project_task_panel->update_left_time(obj->mc_left_time);
if (obj->subtask_) {
@@ -3150,6 +3212,7 @@ void StatusPanel::update_subtask(MachineObject *obj)
if (obj->is_printing_finished()) {
obj->update_model_task();
m_project_task_panel->enable_abort_button(false);
m_project_task_panel->enable_partskip_button(nullptr, false);
m_project_task_panel->enable_pause_resume_button(false, "resume_disable");
// is makeworld subtask
if (wxGetApp().has_model_mall() && obj->is_makeworld_subtask()) {
@@ -3217,6 +3280,32 @@ void StatusPanel::update_subtask(MachineObject *obj)
Layout();
}
void StatusPanel::update_partskip_subtask(MachineObject *obj){
if (!obj) return;
if (!obj->subtask_) return;
auto partskip_button = m_project_task_panel->get_partskip_button();
if (partskip_button) {
int part_cnt = 0;
if(m_project_task_panel->get_part_skipped_dirty() > 0){
m_project_task_panel->set_part_skipped_dirty(m_project_task_panel->get_part_skipped_dirty() - 1);
part_cnt = m_project_task_panel->get_part_skipped_count();
BOOST_LOG_TRIVIAL(info) << "part skip: stop recv printer dirty data.";
}else{
part_cnt = obj->m_partskip_ids.size();
BOOST_LOG_TRIVIAL(info) << "part skip: recv printer normal data.";
}
if (part_cnt > 0)
partskip_button->SetLabel(wxString::Format(_L("(%d)"), part_cnt));
else
partskip_button->SetLabel("");
}
if(m_partskip_dlg && m_partskip_dlg->IsShown()) {
m_partskip_dlg->UpdatePartsStateFromPrinter(obj);
}
}
void StatusPanel::update_cloud_subtask(MachineObject *obj)
{
if (!obj) return;
@@ -3281,6 +3370,7 @@ void StatusPanel::update_sdcard_subtask(MachineObject *obj)
void StatusPanel::reset_printing_values()
{
m_project_task_panel->enable_partskip_button(nullptr, false);
m_project_task_panel->enable_pause_resume_button(false, "pause_disable");
m_project_task_panel->enable_abort_button(false);
m_project_task_panel->reset_printing_value();

View File

@@ -32,6 +32,7 @@
#include "Widgets/AMSControl.hpp"
#include "Widgets/FanControl.hpp"
#include "HMS.hpp"
#include "PartSkipDialog.hpp"
class StepIndicator;
@@ -160,7 +161,7 @@ public:
private:
MachineObject* m_obj;
MachineObject* m_obj{nullptr};
ScalableBitmap m_thumbnail_placeholder;
wxBitmap m_thumbnail_bmp_display;
ScalableBitmap m_bitmap_use_time;
@@ -192,6 +193,7 @@ private:
wxStaticBitmap* m_bitmap_static_use_weight;
ScalableButton* m_button_pause_resume;
ScalableButton* m_button_abort;
Button* m_button_partskip;
Button* m_button_market_scoring;
Button* m_button_clean;
Button * m_button_market_retry;
@@ -203,6 +205,10 @@ private:
std::vector<ScalableButton *> m_score_star;
bool m_star_count_dirty = false;
// partskip button
int m_part_skipped_count{ 0 };
int m_part_skipped_dirty{ 0 };
ProgressBar* m_gauge_progress;
Label* m_error_text;
PrintingTaskType m_type;
@@ -217,6 +223,7 @@ public:
void msw_rescale();
public:
void enable_partskip_button(MachineObject* obj, bool enable);
void enable_pause_resume_button(bool enable, std::string type);
void enable_abort_button(bool enable);
void update_subtask_name(wxString name);
@@ -236,6 +243,7 @@ public:
public:
ScalableButton* get_abort_button() {return m_button_abort;};
ScalableButton* get_pause_resume_button() {return m_button_pause_resume;};
Button* get_partskip_button() { return m_button_partskip; };
Button* get_market_scoring_button() {return m_button_market_scoring;};
Button * get_market_retry_buttom() { return m_button_market_retry; };
Button* get_clean_button() {return m_button_clean;};
@@ -246,6 +254,10 @@ public:
std::vector<ScalableButton *> &get_score_star() { return m_score_star; }
bool get_star_count_dirty() { return m_star_count_dirty; }
void set_star_count_dirty(bool dirty) { m_star_count_dirty = dirty; }
int get_part_skipped_count() { return m_part_skipped_count; }
void set_part_skipped_count(int count) { m_part_skipped_count = count; }
int get_part_skipped_dirty() { return m_part_skipped_dirty; }
void set_part_skipped_dirty(int dirty) { m_part_skipped_dirty = dirty; }
void set_has_reted_text(bool has_rated);
void paint(wxPaintEvent&);
};
@@ -328,6 +340,7 @@ protected:
wxStaticText * m_staticText_progress_left;
wxStaticText * m_staticText_layers;
Button * m_button_report;
Button * m_button_partskip;
ScalableButton *m_button_pause_resume;
ScalableButton *m_button_abort;
Button * m_button_clean;
@@ -402,6 +415,7 @@ protected:
PrintingTaskPanel * m_project_task_panel;
// Virtual event handlers, override them in your derived class
virtual void on_subtask_partskip(wxCommandEvent &event) { event.Skip(); }
virtual void on_subtask_pause_resume(wxCommandEvent &event) { event.Skip(); }
virtual void on_subtask_abort(wxCommandEvent &event) { event.Skip(); }
virtual void on_lamp_switch(wxCommandEvent &event) { event.Skip(); }
@@ -484,6 +498,7 @@ protected:
FanControlPopup* m_fan_control_popup{nullptr};
ExtrusionCalibration *m_extrusion_cali_dlg{nullptr};
PartSkipDialog *m_partskip_dlg{nullptr};
wxString m_request_url;
bool m_start_loading_thumbnail = false;
@@ -525,6 +540,7 @@ protected:
void on_market_scoring(wxCommandEvent &event);
void on_market_retry(wxCommandEvent &event);
void on_subtask_partskip(wxCommandEvent &event);
void on_subtask_pause_resume(wxCommandEvent &event);
void on_subtask_abort(wxCommandEvent &event);
void on_print_error_clean(wxCommandEvent &event);
@@ -602,6 +618,7 @@ protected:
void update_basic_print_data(bool def = false);
void update_model_info();
void update_subtask(MachineObject* obj);
void update_partskip_subtask(MachineObject *obj);
void update_cloud_subtask(MachineObject *obj);
void update_sdcard_subtask(MachineObject *obj);
void update_temp_ctrl(MachineObject *obj);
@@ -621,6 +638,9 @@ protected:
void update_camera_state(MachineObject* obj);
bool show_vcamera = false;
// partskip button
void update_partskip_button(MachineObject* obj);
public:
void update_error_message();

View File

@@ -0,0 +1,77 @@
#include "AnimaController.hpp"
#include <wx/dcclient.h>
#include <wx/dcgraph.h>
#ifdef __APPLE__
#include "libslic3r/MacUtils.hpp"
#endif
AnimaIcon::AnimaIcon(wxWindow *parent, wxWindowID id, std::vector<std::string> img_list, std::string img_enable, int ivt)
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize), m_ivt(ivt)
{
auto sizer = new wxBoxSizer(wxHORIZONTAL);
SetBackgroundColour((wxColour(255, 255, 255)));
m_size = 25;
//add ScalableBitmap
for (const auto &filename : img_list) m_images.emplace_back(create_scaled_bitmap(filename, this, m_size));
m_image_enable = create_scaled_bitmap(img_enable, this, m_size-8);
// show first wxStaticBitmap
if (!m_images.empty()) m_bitmap = new wxStaticBitmap(this, wxID_ANY, m_images[0], wxDefaultPosition, wxSize(FromDIP(m_size), FromDIP(m_size)));
m_timer = new wxTimer();
m_timer->SetOwner(this);
Bind(wxEVT_TIMER, [this](wxTimerEvent &) {
if (m_timer->IsRunning() && !m_images.empty()) {
m_current_frame = (m_current_frame + 1) % 4;
m_bitmap->SetBitmap(m_images[m_current_frame]);
}
});
m_bitmap->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) {
wxMouseEvent evt(wxEVT_LEFT_DOWN);
evt.SetEventObject(this);
wxPostEvent(this, evt);
});
m_bitmap->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) {
if (!m_timer->IsRunning())
SetCursor(wxCursor(wxCURSOR_HAND));
else
SetCursor(wxCursor(wxCURSOR_ARROW));
e.Skip();
});
m_bitmap->Bind(wxEVT_LEAVE_WINDOW, [this](auto &e) {
SetCursor(wxCursor(wxCURSOR_ARROW));
e.Skip();
});
sizer->Add(m_bitmap, 0, wxALIGN_CENTER, 0);
SetSizer(sizer);
SetSize(wxSize(FromDIP(m_size), FromDIP(m_size)));
SetMaxSize(wxSize(FromDIP(m_size), FromDIP(m_size)));
SetMinSize(wxSize(FromDIP(m_size), FromDIP(m_size)));
Layout();
Fit();
Play();
}
void AnimaIcon::Play()
{
if (true)
m_timer->Start(m_ivt);
}
void AnimaIcon::Stop()
{
m_timer->Stop();
}
void AnimaIcon::Enable()
{
if (m_bitmap) { m_bitmap->SetBitmap(m_image_enable); }
}

View File

@@ -0,0 +1,28 @@
#ifndef slic3r_GUI_AnimaController_hpp_
#define slic3r_GUI_AnimaController_hpp_
#include "../wxExtensions.hpp"
#include "Label.hpp"
class AnimaIcon : public wxPanel
{
public:
AnimaIcon(wxWindow *parent, wxWindowID id, std::vector<std::string> img_list, std::string img_enable, int ivt = 1000);
void Play();
void Stop();
void Enable();
bool IsRunning() const;
private:
wxBitmap m_image_enable;
wxStaticBitmap * m_bitmap{nullptr};
std::vector<wxBitmap> m_images;
wxTimer * m_timer;
int m_current_frame = 0;
int m_ivt;
int m_size;
};
#endif // !slic3r_GUI_AnimaController_hpp_

View File

@@ -15,7 +15,7 @@ ProgressBar::ProgressBar(wxWindow *parent, wxWindowID id, int max, const wxPoint
{
m_shownumber = shown;
SetBackgroundColour(wxColour(255,255,255));
if (size.y >= miniHeight) {
m_miniHeight = size.y;
} else {
@@ -64,20 +64,20 @@ void ProgressBar::create(wxWindow *parent, wxWindowID id, const wxPoint &pos, w
}
void ProgressBar::SetRadius(double radius) {
void ProgressBar::SetRadius(double radius) {
m_radius = radius;
Refresh();
}
void ProgressBar::SetProgressForedColour(wxColour colour)
void ProgressBar::SetProgressForedColour(wxColour colour)
{
m_progress_background_colour = colour;
Refresh();
}
void ProgressBar::SetProgressBackgroundColour(wxColour colour)
{
m_progress_colour = colour;
void ProgressBar::SetProgressBackgroundColour(wxColour colour)
{
m_progress_colour = colour;
Refresh();
}
@@ -86,29 +86,29 @@ void ProgressBar::Rescale()
;
}
void ProgressBar::ShowNumber(bool shown)
void ProgressBar::ShowNumber(bool shown)
{
m_shownumber = shown;
Refresh();
}
void ProgressBar::Disable(wxString text)
{
void ProgressBar::Disable(wxString text)
{
if (m_disable) return;
m_disable_text = text;
m_disable = true;
Refresh();
}
void ProgressBar::SetValue(int step)
{
void ProgressBar::SetValue(int step)
{
m_disable = false;
SetProgress(step);
}
void ProgressBar::Reset()
{
m_step = 0;
void ProgressBar::Reset()
{
m_step = 0;
SetValue(0);
}
@@ -122,9 +122,9 @@ void ProgressBar::SetProgress(int step)
}
void ProgressBar::SetMinSize(const wxSize &size)
{
if (size.y >= miniHeight) {
void ProgressBar::SetMinSize(const wxSize &size)
{
if (size.y >= miniHeight) {
m_miniHeight = size.y;
} else {
return;
@@ -177,7 +177,7 @@ void ProgressBar::doRender(wxDC &dc)
dc.DrawRoundedRectangle(0, 0, size.x, size.y, m_radius);
}
//draw progress
//draw progress
if (m_disable) {
m_proportion = float(size.x * float(this->m_step) / float(this->m_max));
if (m_proportion < m_radius * 2 && m_proportion != 0) { m_proportion = m_radius * 2; }
@@ -228,11 +228,11 @@ void ProgressBar::doRender(wxDC &dc)
dc.DrawText(text + wxString("%"), pt);
}
}
}
void ProgressBar::DoSetSize(int x, int y, int width, int height, int sizeFlags)
{
void ProgressBar::DoSetSize(int x, int y, int width, int height, int sizeFlags)
{
wxWindow::DoSetSize(x, y, width, height, sizeFlags);
}