Merge branch 'main' into dev/h2d-2
# Conflicts: # localization/i18n/list.txt # src/slic3r/GUI/CalibrationWizardPresetPage.cpp # src/slic3r/GUI/DeviceManager.cpp # src/slic3r/GUI/DeviceManager.hpp # src/slic3r/GUI/Printer/PrinterFileSystem.cpp # src/slic3r/GUI/Printer/PrinterFileSystem.h # src/slic3r/GUI/SelectMachine.hpp # src/slic3r/GUI/SendToPrinter.cpp # src/slic3r/GUI/SendToPrinter.hpp # src/slic3r/GUI/StatusPanel.hpp # src/slic3r/GUI/Widgets/AnimaController.cpp
3
.gitignore
vendored
@@ -39,3 +39,6 @@ src/OrcaSlicer-doc/
|
||||
resources/profiles/user/default
|
||||
*.code-workspace
|
||||
deps_src/build/
|
||||
test.js
|
||||
/.cache/
|
||||
.clangd
|
||||
|
||||
@@ -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)
|
||||
|
||||
27
deps_src/earcut/CHANGELOG.md
Normal 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
|
||||
13
deps_src/earcut/CMakeLists.txt
Normal 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
@@ -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
@@ -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.
|
||||
|
||||
[](https://travis-ci.com/github/mapbox/earcut.hpp)
|
||||
[](https://ci.appveyor.com/project/Mapbox/earcut-hpp-8wm4o/branch/master)
|
||||
[](https://coveralls.io/github/mapbox/earcut.hpp)
|
||||
[](https://scan.coverity.com/projects/14000)
|
||||
[](http://isitmaintained.com/project/mapbox/earcut.hpp "Average time to resolve an issue")
|
||||
[](http://isitmaintained.com/project/mapbox/earcut.hpp "Percentage of issues still open")
|
||||
[](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
@@ -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 = ¤tBlock[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);
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ Support structures are used in 3D printing to provide stability to overhangs and
|
||||
- [Initial layer density](#initial-layer-density)
|
||||
- [Initial layer expansion](#initial-layer-expansion)
|
||||
- [On build plate only](#on-build-plate-only)
|
||||
- [Remove small overhangs](#remove-small-overhangs)
|
||||
- [Ignore small overhangs](#ignore-small-overhangs)
|
||||
|
||||
## Type
|
||||
|
||||
@@ -95,6 +95,6 @@ Expand the first raft or support layer to improve bed plate adhesion.
|
||||
|
||||
Don't create support on model surface, only on build plate.
|
||||
|
||||
## Remove small overhangs
|
||||
## Ignore small overhangs
|
||||
|
||||
Remove small overhangs that possibly need no supports.
|
||||
With this setting small overhangs that possibly need no supports will be ignored from support generation.
|
||||
|
||||
@@ -12802,10 +12802,10 @@ msgid ""
|
||||
"etc."
|
||||
msgstr ""
|
||||
|
||||
msgid "Remove small overhangs"
|
||||
msgid "Ignore small overhangs"
|
||||
msgstr ""
|
||||
|
||||
msgid "Remove small overhangs that possibly need no supports."
|
||||
msgid "Ignore small overhangs that possibly don't require support."
|
||||
msgstr ""
|
||||
|
||||
msgid "Top Z distance"
|
||||
|
||||
@@ -234,6 +234,10 @@ 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
|
||||
src/slic3r/GUI/FilamentBitmapUtils.cpp
|
||||
src/slic3r/GUI/FilamentBitmapUtils.hpp
|
||||
src/slic3r/GUI/FilamentPickerDialog.cpp
|
||||
|
||||
3
resources/images/canvas_drag.svg
Normal 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 |
3
resources/images/canvas_drag_active.svg
Normal 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 |
3
resources/images/canvas_zoom_in.svg
Normal 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 |
3
resources/images/canvas_zoom_in_disable.svg
Normal 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 |
3
resources/images/canvas_zoom_out.svg
Normal 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 |
3
resources/images/canvas_zoom_out_disable.svg
Normal 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 |
26
resources/images/partskip_retry.svg
Normal 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 |
4
resources/images/print_control_partskip.svg
Normal 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 |
4
resources/images/print_control_partskip_disable.svg
Normal 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 |
4
resources/images/print_control_partskip_hover.svg
Normal 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 |
@@ -5415,9 +5415,9 @@ void PrintConfigDef::init_fff_params()
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("support_remove_small_overhang", coBool);
|
||||
def->label = L("Remove small overhangs");
|
||||
def->label = L("Ignore small overhangs");
|
||||
def->category = L("Support");
|
||||
def->tooltip = L("Remove small overhangs that possibly need no supports.");
|
||||
def->tooltip = L("Ignore small overhangs that possibly don't require support.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
|
||||
@@ -405,6 +405,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
|
||||
|
||||
@@ -1336,6 +1336,17 @@ int MachineObject::command_go_home()
|
||||
return this->is_in_printing() ? this->publish_gcode("G28 X\n") : this->publish_gcode("G28 \n");
|
||||
}
|
||||
|
||||
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, 1);
|
||||
}
|
||||
|
||||
int MachineObject::command_task_abort()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "command_task_abort: ";
|
||||
@@ -2313,6 +2324,7 @@ void MachineObject::reset()
|
||||
}
|
||||
}
|
||||
subtask_ = nullptr;
|
||||
m_partskip_ids.clear();
|
||||
}
|
||||
|
||||
void MachineObject::set_print_state(std::string status)
|
||||
@@ -2567,6 +2579,29 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
|
||||
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"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4407,6 +4442,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;
|
||||
@@ -4469,8 +4505,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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -4880,6 +4915,7 @@ void MachineObject::parse_new_info(json print)
|
||||
is_support_airprinting_detection = get_flag_bits(fun, 45);
|
||||
m_fan->SetSupportCoolingFilter(get_flag_bits(fun, 46));
|
||||
is_support_ext_change_assist = get_flag_bits(fun, 48);
|
||||
is_support_partskip = get_flag_bits(fun, 49);
|
||||
}
|
||||
|
||||
/*aux*/
|
||||
|
||||
@@ -554,6 +554,9 @@ public:
|
||||
time_t xcam_prompt_sound_hold_start = 0;
|
||||
time_t xcam_filament_tangle_detect_hold_start = 0;
|
||||
|
||||
// part skip
|
||||
std::vector<int> m_partskip_ids;
|
||||
|
||||
/*target from Studio-SwitchBoard, default to INVALID_NOZZLE_ID if no switching control from PC*/
|
||||
int targ_nozzle_id_from_pc = INVALID_EXTRUDER_ID;
|
||||
|
||||
@@ -593,6 +596,7 @@ public:
|
||||
bool m_support_mqtt_homing { false };// fun[32]
|
||||
bool is_support_brtc{false}; // fun[31], support tcp and upload protocol
|
||||
bool is_support_ext_change_assist{false};
|
||||
bool is_support_partskip{false};
|
||||
|
||||
// refine printer function options
|
||||
bool is_support_spaghetti_detection{false};
|
||||
@@ -689,6 +693,7 @@ public:
|
||||
|
||||
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();
|
||||
|
||||
18
src/slic3r/GUI/PartSkipCommon.hpp
Normal 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
|
||||
1049
src/slic3r/GUI/PartSkipDialog.cpp
Normal file
163
src/slic3r/GUI/PartSkipDialog.hpp
Normal 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
|
||||
@@ -39,6 +39,7 @@ 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);
|
||||
@@ -271,6 +272,149 @@ struct PrinterFileSystem::Upload : Progress
|
||||
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) {
|
||||
@@ -304,6 +448,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) {
|
||||
@@ -1280,7 +1428,7 @@ void PrinterFileSystem::CancelUploadTask(bool send_cancel_req)
|
||||
}
|
||||
}
|
||||
|
||||
boost::uint32_t PrinterFileSystem::SendRequest(int type, json const &req, callback_t2 const &callback)
|
||||
boost::uint32_t PrinterFileSystem::SendRequest(int type, json const &req, callback_t2 const &callback,const std::string& param)
|
||||
{
|
||||
if (m_session.tunnel == nullptr) {
|
||||
Retry();
|
||||
@@ -1294,6 +1442,13 @@ boost::uint32_t PrinterFileSystem::SendRequest(int type, json const &req, callba
|
||||
root["req"] = req;
|
||||
std::ostringstream oss;
|
||||
oss << root;
|
||||
|
||||
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);
|
||||
|
||||
@@ -22,6 +22,7 @@ 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);
|
||||
@@ -183,6 +184,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 ¶m);
|
||||
|
||||
void SendExistedFile();
|
||||
|
||||
void SendConnectFail();
|
||||
|
||||
void DownloadCheckFiles(std::string const &path);
|
||||
|
||||
bool DownloadCheckFile(size_t index);
|
||||
@@ -275,7 +287,7 @@ private:
|
||||
|
||||
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)
|
||||
template<typename T> boost::uint32_t SendRequest(int type, json const &req, Translator<T> const &translator, Callback<T> const &callback, const std::string ¶m = "")
|
||||
{
|
||||
auto c = [translator, callback, this](int result, json const &resp, unsigned char const *data) -> int
|
||||
{
|
||||
@@ -292,7 +304,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 &)>;
|
||||
@@ -322,7 +334,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 ¶m = "");
|
||||
|
||||
void InstallNotify(int type, callback_t2 const &callback);
|
||||
|
||||
@@ -365,6 +377,8 @@ private:
|
||||
size_t m_lock_end = 0;
|
||||
int m_task_flags = 0;
|
||||
|
||||
std::vector<bool> m_download_states;
|
||||
|
||||
private:
|
||||
struct Session
|
||||
{
|
||||
|
||||
697
src/slic3r/GUI/SkipPartCanvas.cpp
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
169
src/slic3r/GUI/SkipPartCanvas.hpp
Normal 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
|
||||
@@ -584,12 +584,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));
|
||||
@@ -612,24 +606,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")) {
|
||||
@@ -652,7 +656,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) {
|
||||
@@ -663,19 +667,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);
|
||||
@@ -708,55 +707,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));
|
||||
@@ -764,7 +773,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();
|
||||
@@ -994,6 +1002,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) {
|
||||
@@ -2248,6 +2274,7 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co
|
||||
//m_switch_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);
|
||||
|
||||
@@ -2278,6 +2305,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);
|
||||
@@ -2338,6 +2366,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);
|
||||
@@ -2472,6 +2501,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) {
|
||||
@@ -3609,13 +3667,17 @@ void StatusPanel::update_subtask(MachineObject *obj)
|
||||
m_project_task_panel->show_layers_num(obj->is_support_layer_num);
|
||||
|
||||
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;
|
||||
@@ -3657,7 +3719,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_) {
|
||||
@@ -3674,6 +3736,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()) {
|
||||
@@ -3741,6 +3804,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;
|
||||
@@ -3807,6 +3896,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();
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "Widgets/FilamentLoad.hpp"
|
||||
#include "Widgets/FanControl.hpp"
|
||||
#include "HMS.hpp"
|
||||
#include "PartSkipDialog.hpp"
|
||||
#include "DeviceErrorDialog.hpp"
|
||||
|
||||
class StepIndicator;
|
||||
@@ -260,7 +261,7 @@ public:
|
||||
|
||||
|
||||
private:
|
||||
MachineObject* m_obj;
|
||||
MachineObject* m_obj{nullptr};
|
||||
ScalableBitmap m_thumbnail_placeholder;
|
||||
wxBitmap m_thumbnail_bmp_display;
|
||||
ScalableBitmap m_bitmap_use_time;
|
||||
@@ -292,6 +293,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;
|
||||
@@ -303,6 +305,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;
|
||||
@@ -317,6 +323,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);
|
||||
@@ -337,6 +344,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;};
|
||||
@@ -347,6 +355,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&);
|
||||
};
|
||||
@@ -432,6 +444,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;
|
||||
@@ -526,6 +539,7 @@ protected:
|
||||
StaticBox* m_filament_load_box;
|
||||
|
||||
// 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(); }
|
||||
@@ -612,6 +626,7 @@ protected:
|
||||
FanControlPopupNew* 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;
|
||||
@@ -652,6 +667,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);
|
||||
@@ -728,6 +744,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);
|
||||
@@ -747,6 +764,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();
|
||||
|
||||
|
||||
@@ -2529,7 +2529,7 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("raft_first_layer_expansion", "support_settings_support#initial-layer-expansion");
|
||||
optgroup->append_single_option_line("support_on_build_plate_only", "support_settings_support#on-build-plate-only");
|
||||
optgroup->append_single_option_line("support_critical_regions_only", "support_settings_support#support-critical-regions-only");
|
||||
optgroup->append_single_option_line("support_remove_small_overhang", "support_settings_support#remove-small-overhangs");
|
||||
optgroup->append_single_option_line("support_remove_small_overhang", "support_settings_support#ignore-small-overhangs");
|
||||
//optgroup->append_single_option_line("enforce_support_layers", "support_settings_support");
|
||||
|
||||
optgroup = page->new_optgroup(L("Raft"), L"param_raft");
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
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 = 20;
|
||||
m_size = 25;
|
||||
|
||||
//add ScalableBitmap
|
||||
for (const auto &filename : img_list) m_images.emplace_back(create_scaled_bitmap(filename, this, m_size));
|
||||
@@ -47,12 +48,13 @@ AnimaIcon::AnimaIcon(wxWindow *parent, wxWindowID id, std::vector<std::string> i
|
||||
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)));
|
||||
Refresh();
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
Play();
|
||||
}
|
||||
|
||||
@@ -73,5 +75,3 @@ void AnimaIcon::Enable()
|
||||
{
|
||||
if (m_bitmap) { m_bitmap->SetBitmap(m_image_enable); }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -813,7 +813,7 @@ void ProgressDialog::DoSetSize(int x, int y, int width, int height, int sizeFlag
|
||||
// m_block_right->SetPosition(wxPoint(PROGRESSDIALOG_GAUGE_SIZE.x - 2, 0));
|
||||
//}
|
||||
#endif
|
||||
wxWindow::DoSetSize(x, y, width, height, sizeFlags);
|
||||
wxDialog::DoSetSize(x, y, width, height, sizeFlags);
|
||||
}
|
||||
|
||||
void ProgressDialog::DisableOtherWindows()
|
||||
|
||||