mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-15 20:45:55 +08:00
Merge branch 'master' of https://github.com/Prusa-Development/PrusaSlicerPrivate into et_mirror
This commit is contained in:
commit
fd956ecc49
@ -1,28 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
|
||||
<g id="cut">
|
||||
<!-- Generator: Adobe Illustrator 27.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<path fill="#ED6B21" d="M3.0597045,10.3634434H0.5884437C0.2628375,10.3634434,0,10.100606,0,9.7750006
|
||||
c0-0.3256063,0.2628375-0.5884438,0.5884437-0.5884438h2.4712608c0.3256061,0,0.5884435,0.2628374,0.5884435,0.5884438
|
||||
C3.6481481,10.100606,3.3853078,10.3634434,3.0597045,10.3634434z"/>
|
||||
<path fill="#ED6B21" d="M12.0967369,10.3634434h-2.471261c-0.3256063,0-0.5884438-0.2628374-0.5884438-0.5884428
|
||||
c0-0.3256063,0.2628374-0.5884438,0.5884438-0.5884438h2.471261c0.3256063,0,0.5884438,0.2628374,0.5884438,0.5884438
|
||||
C12.6851807,10.100606,12.4223404,10.3634434,12.0967369,10.3634434z"/>
|
||||
<path fill="#ED6B21" d="M7.5782208,10.3634434h-2.471261c-0.3256059,0-0.5884438-0.2628374-0.5884438-0.5884428
|
||||
c0-0.3256063,0.2628379-0.5884438,0.5884438-0.5884438h2.471261c0.3256059,0,0.5884433,0.2628374,0.5884433,0.5884438
|
||||
C8.1666641,10.100606,7.9038239,10.3634434,7.5782208,10.3634434z"/>
|
||||
<g>
|
||||
<path fill="#ED6B21" d="M118.12,65.5h-10c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5h10c0.83,0,1.5,0.67,1.5,1.5
|
||||
S118.95,65.5,118.12,65.5z M98.12,65.5h-10c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5h10c0.83,0,1.5,0.67,1.5,1.5
|
||||
S98.95,65.5,98.12,65.5z M78.12,65.5h-10c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5h10c0.83,0,1.5,0.67,1.5,1.5
|
||||
S78.95,65.5,78.12,65.5z M58.12,65.5h-10c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5h10c0.83,0,1.5,0.67,1.5,1.5
|
||||
S58.95,65.5,58.12,65.5z M38.12,65.5h-10c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5h10c0.83,0,1.5,0.67,1.5,1.5
|
||||
S38.95,65.5,38.12,65.5z M18.12,65.5h-10c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5h10c0.83,0,1.5,0.67,1.5,1.5
|
||||
S18.95,65.5,18.12,65.5z"/>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#808080" d="M108.79,51.6H19.21c-1.93,0-3.5-1.57-3.5-3.5V10.12c0-1.93,1.57-3.5,3.5-3.5h89.57
|
||||
c1.93,0,3.5,1.57,3.5,3.5V48.1C112.29,50.03,110.71,51.6,108.79,51.6z M19.21,9.62c-0.27,0-0.5,0.23-0.5,0.5V48.1
|
||||
c0,0.27,0.23,0.5,0.5,0.5h89.57c0.27,0,0.5-0.23,0.5-0.5V10.12c0-0.27-0.23-0.5-0.5-0.5H19.21z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#808080" d="M108.79,121.38H19.21c-1.93,0-3.5-1.57-3.5-3.5V79.4c0-1.93,1.57-3.5,3.5-3.5h89.57
|
||||
c1.93,0,3.5,1.57,3.5,3.5v38.49C112.29,119.81,110.71,121.38,108.79,121.38z M19.21,78.9c-0.27,0-0.5,0.23-0.5,0.5v38.49
|
||||
c0,0.27,0.23,0.5,0.5,0.5h89.57c0.27,0,0.5-0.23,0.5-0.5V79.4c0-0.27-0.23-0.5-0.5-0.5H19.21z"/>
|
||||
</g>
|
||||
</g>
|
||||
<path fill="#808080" d="M10.98001,11.9849854c-0.289978,0-0.5199585,0.2299805-0.5199585,0.5199585v0.5922852v0.3327637
|
||||
c0,0.289978-0.2300415,0.5200195-0.5200195,0.5200195H2.7452075c-0.289978,0-0.5200195-0.2300415-0.5200195-0.5200195V13.097229
|
||||
v-0.5922852c0-0.289978-0.2299803-0.5199585-0.5199584-0.5199585c-0.2900391,0-0.5200195,0.2299805-0.5200195,0.5199585v0.5922852
|
||||
v0.3327637C1.1852101,14.2999878,1.8952321,15,2.7552173,15h7.1748047c0.8599854,0,1.5700073-0.7000122,1.5700073-1.5700073
|
||||
V13.097229v-0.5922852C11.5000296,12.2149658,11.2700491,11.9849854,10.98001,11.9849854z"/>
|
||||
<path fill="#808080" d="M9.9300222,4.5499878H2.7552173c-0.8599852,0-1.5700072,0.7000122-1.5700072,1.5700073v0.3327637v0.5922852
|
||||
c0,0.289978,0.2299805,0.5199585,0.5200195,0.5199585c0.289978,0,0.5199584-0.2299805,0.5199584-0.5199585V6.4527588V6.1199951
|
||||
c0-0.289978,0.2300415-0.5200195,0.5200195-0.5200195H9.940032c0.289978,0,0.5200195,0.2300415,0.5200195,0.5200195v0.3327637
|
||||
v0.5922852c0,0.289978,0.2299805,0.5199585,0.5199585,0.5199585c0.2900391,0,0.5200195-0.2299805,0.5200195-0.5199585V6.4527588
|
||||
V6.1199951C11.5000296,5.25,10.7900076,4.5499878,9.9300222,4.5499878z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.4 KiB |
@ -1,26 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<g id="add_x5F_part">
|
||||
<!-- Generator: Adobe Illustrator 27.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<path fill="#ED6B21" d="M7.2122064,10.8825388H5.473033c-0.6128426,0-1.1075444-0.4947023-1.1075444-1.1075449
|
||||
S4.8601904,8.667449,5.473033,8.667449h1.7391734c0.6128426,0,1.1075444,0.4947023,1.1075444,1.1075449
|
||||
S7.8250437,10.8825388,7.2122064,10.8825388z"/>
|
||||
<g>
|
||||
<path fill="#ED6B21" d="M14.62,4.37c-0.01-0.14,0.06-0.34,0.15-0.44l0.13-0.15c0.09-0.11,0.12-0.3,0.07-0.43l-0.2-0.49
|
||||
c-0.05-0.13-0.21-0.24-0.35-0.25l-0.2-0.01c-0.14-0.01-0.33-0.1-0.42-0.21c-0.09-0.1-0.37-0.46-0.38-0.6l-0.01-0.2
|
||||
c-0.01-0.14-0.12-0.3-0.25-0.35l-0.49-0.2C12.52,0.97,12.33,1,12.22,1.1l-0.15,0.13c-0.11,0.09-0.31,0.16-0.44,0.15
|
||||
c-0.14-0.01-0.59-0.06-0.69-0.15L10.78,1.1c-0.11-0.09-0.3-0.12-0.43-0.07l-0.49,0.2C9.73,1.28,9.61,1.44,9.6,1.58l-0.01,0.2
|
||||
C9.58,1.92,9.49,2.11,9.38,2.2c-0.1,0.09-0.46,0.37-0.6,0.38L8.58,2.6c-0.14,0.01-0.3,0.12-0.35,0.25l-0.2,0.49
|
||||
C7.97,3.48,8,3.67,8.1,3.78l0.13,0.15c0.09,0.11,0.16,0.31,0.15,0.44C8.37,4.52,8.32,4.96,8.23,5.07L8.1,5.22
|
||||
C8,5.33,7.97,5.52,8.03,5.65l0.2,0.49C8.28,6.27,8.44,6.39,8.58,6.4l0.2,0.01c0.14,0.01,0.33,0.1,0.42,0.21
|
||||
c0.09,0.1,0.37,0.46,0.38,0.6l0.01,0.2c0.01,0.14,0.12,0.3,0.25,0.35l0.49,0.2C10.48,8.03,10.67,8,10.78,7.9l0.15-0.13
|
||||
c0.11-0.09,0.31-0.16,0.44-0.15c0.14,0.01,0.59,0.06,0.69,0.15l0.15,0.13c0.11,0.09,0.3,0.12,0.43,0.07l0.49-0.2
|
||||
c0.13-0.05,0.24-0.21,0.25-0.35l0.01-0.2c0.01-0.14,0.1-0.33,0.21-0.42s0.46-0.37,0.6-0.38l0.2-0.01c0.14-0.01,0.3-0.12,0.35-0.25
|
||||
l0.2-0.49C15.03,5.52,15,5.33,14.9,5.22l-0.13-0.15C14.68,4.96,14.63,4.51,14.62,4.37z M11.5,6.6c-1.16,0-2.1-0.94-2.1-2.1
|
||||
s0.94-2.1,2.1-2.1s2.1,0.94,2.1,2.1S12.66,6.6,11.5,6.6z"/>
|
||||
</g>
|
||||
<path fill="#808080" d="M10.98,9.78c-0.29,0-0.52,0.23-0.52,0.52v2.09v1.04c0,0.29-0.23,0.52-0.52,0.52H2.62
|
||||
c-0.29,0-0.53-0.24-0.53-0.53L2.04,6.12c0-0.14,0.05-0.27,0.15-0.37c0.1-0.1,0.23-0.15,0.37-0.15l3.19,0v0
|
||||
c0.29,0,0.52-0.23,0.52-0.52S6.04,4.55,5.75,4.55H3.66c-0.01,0-0.01,0-0.02,0l-1.08,0c-0.42,0-0.81,0.16-1.11,0.46
|
||||
C1.16,5.31,1,5.71,1,6.13l0.04,7.31C1.05,14.3,1.75,15,2.62,15h7.31c0.86,0,1.57-0.7,1.57-1.57v-1.04V10.3
|
||||
C11.5,10.01,11.27,9.78,10.98,9.78z"/>
|
||||
<path fill="#808080" d="M10.98001,15c-0.289978,0-0.5199585-0.2299805-0.5199585-0.5199585v-0.5922852v-0.3327637
|
||||
c0-0.289978-0.2300415-0.5200195-0.5200195-0.5200195H2.7452075c-0.289978,0-0.5200195,0.2300415-0.5200195,0.5200195v0.3327637
|
||||
v0.5922852C2.225188,14.7700195,1.9952077,15,1.7052296,15c-0.2900391,0-0.5200195-0.2299805-0.5200195-0.5199585v-0.5922852
|
||||
v-0.3327637c0-0.8699951,0.710022-1.5700073,1.5700072-1.5700073h7.1748047c0.8599854,0,1.5700073,0.7000122,1.5700073,1.5700073
|
||||
v0.3327637v0.5922852C11.5000296,14.7700195,11.2700491,15,10.98001,15z"/>
|
||||
<path fill="#808080" d="M9.9300222,7.5650024H2.7552173c-0.8599852,0-1.5700072-0.7000122-1.5700072-1.5700073V5.6622314V5.0699463
|
||||
c0-0.289978,0.2299805-0.5199585,0.5200195-0.5199585c0.289978,0,0.5199584,0.2299805,0.5199584,0.5199585v0.5922852v0.3327637
|
||||
c0,0.289978,0.2300415,0.5200195,0.5200195,0.5200195H9.940032c0.289978,0,0.5200195-0.2300415,0.5200195-0.5200195V5.6622314
|
||||
V5.0699463c0-0.289978,0.2299805-0.5199585,0.5199585-0.5199585c0.2900391,0,0.5200195,0.2299805,0.5200195,0.5199585v0.5922852
|
||||
v0.3327637C11.5000296,6.8649902,10.7900076,7.5650024,9.9300222,7.5650024z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 1.7 KiB |
BIN
resources/profiles/Creality/ENDER5S1_thumbnail.png
Normal file
BIN
resources/profiles/Creality/ENDER5S1_thumbnail.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
BIN
resources/profiles/Creality/SERMOONV1_thumbnail.png
Normal file
BIN
resources/profiles/Creality/SERMOONV1_thumbnail.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
@ -210,8 +210,9 @@ bool its_write_obj(const indexed_triangle_set &its, const char *file)
|
||||
bool its_write_obj(const indexed_triangle_set& its, const std::vector<obj_color> &color, const char* file)
|
||||
{
|
||||
Slic3r::CNumericLocalesSetter locales_setter;
|
||||
FILE* fp = fopen(file, "w");
|
||||
FILE* fp = boost::nowide::fopen(file, "w");
|
||||
if (fp == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "stl_write_obj: Couldn't open " << file << " for writing";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,12 @@ imstb_truetype.h modification:
|
||||
|
||||
Hot fix for open symbolic fonts on windows
|
||||
62bdfe6f8d04b88e8bd511cd613be80c0baa7f55
|
||||
Add case STBTT_MS_EID_SYMBOL to swith in file imstb_truetype.h on line 1440.
|
||||
|
||||
Hot fix for open curved fonts mainly on MAC
|
||||
2148e49f75d82cb19dc6ec409fb7825296ed005c
|
||||
viz. https://github.com/nothings/stb/issues/1296
|
||||
In file imstb_truetype.h line 1667 change malloc size from:
|
||||
vertices = (stbtt_vertex *) STBTT_malloc((m + 1) * sizeof(vertices[0]), info->userdata);
|
||||
to:
|
||||
vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata);
|
@ -174,7 +174,6 @@ public:
|
||||
BoundingBox rotated(double angle, const Point ¢er) const;
|
||||
void rotate(double angle) { (*this) = this->rotated(angle); }
|
||||
void rotate(double angle, const Point ¢er) { (*this) = this->rotated(angle, center); }
|
||||
bool intersects(const BoundingBox &other) const { return this->min(0) <= other.max(0) && this->max(0) >= other.min(0) && this->min(1) <= other.max(1) && this->max(1) >= other.min(1); }
|
||||
// Align the min corner to a grid of cell_size x cell_size cells,
|
||||
// to encompass the original bounding box.
|
||||
void align_to_grid(const coord_t cell_size);
|
||||
|
@ -185,7 +185,6 @@ set(SLIC3R_SOURCES
|
||||
Model.hpp
|
||||
ModelArrange.hpp
|
||||
ModelArrange.cpp
|
||||
#ModelVolumeType.hpp
|
||||
MultiMaterialSegmentation.cpp
|
||||
MultiMaterialSegmentation.hpp
|
||||
MeshNormals.hpp
|
||||
|
@ -283,7 +283,7 @@ ExPolygons Emboss::heal_shape(const Polygons &shape) {
|
||||
|
||||
// Do not remove all duplicits but do it better way
|
||||
// Overlap all duplicit points by rectangle 3x3
|
||||
Points duplicits = collect_duplications(to_points(polygons));
|
||||
Points duplicits = collect_duplicates(to_points(polygons));
|
||||
if (!duplicits.empty()) {
|
||||
polygons.reserve(polygons.size() + duplicits.size());
|
||||
for (const Point &p : duplicits) {
|
||||
@ -310,7 +310,7 @@ bool Emboss::heal_shape(ExPolygons &shape, unsigned max_iteration)
|
||||
priv::remove_same_neighbor(shape);
|
||||
|
||||
Pointfs intersections = intersection_points(shape);
|
||||
Points duplicits = collect_duplications(to_points(shape));
|
||||
Points duplicits = collect_duplicates(to_points(shape));
|
||||
//Points close = priv::collect_close_points(shape, 1.);
|
||||
if (intersections.empty() && duplicits.empty() /* && close.empty() */) break;
|
||||
|
||||
@ -353,7 +353,7 @@ bool Emboss::heal_shape(ExPolygons &shape, unsigned max_iteration)
|
||||
svg.draw(shape, "green");
|
||||
|
||||
svg.draw(duplicits, "lightgray", 13 / Emboss::SHAPE_SCALE);
|
||||
Points duplicits3 = collect_duplications(to_points(shape));
|
||||
Points duplicits3 = collect_duplicates(to_points(shape));
|
||||
svg.draw(duplicits3, "black", 7 / Emboss::SHAPE_SCALE);
|
||||
|
||||
Pointfs pts2 = intersection_points(shape);
|
||||
@ -387,7 +387,7 @@ bool Emboss::heal_shape(ExPolygons &shape, unsigned max_iteration)
|
||||
}
|
||||
|
||||
assert(intersection_points(shape).empty());
|
||||
assert(collect_duplications(to_points(shape)).empty());
|
||||
assert(collect_duplicates(to_points(shape)).empty());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1186,7 +1186,7 @@ indexed_triangle_set Emboss::polygons2model(const ExPolygons &shape2d,
|
||||
const IProjection &projection)
|
||||
{
|
||||
Points points = to_points(shape2d);
|
||||
Points duplicits = collect_duplications(points);
|
||||
Points duplicits = collect_duplicates(points);
|
||||
return (duplicits.empty()) ?
|
||||
priv::polygons2model_unique(shape2d, projection, points) :
|
||||
priv::polygons2model_duplicit(shape2d, projection, points, duplicits);
|
||||
|
@ -148,7 +148,7 @@ static constexpr const char* MESH_STAT_FACETS_RESERVED = "facets_reversed";
|
||||
static constexpr const char* MESH_STAT_BACKWARDS_EDGES = "backwards_edges";
|
||||
|
||||
// Store / load of TextConfiguration
|
||||
static constexpr const char *TEXT_TAG = "emboss";
|
||||
static constexpr const char *TEXT_TAG = "slic3rpe:text";
|
||||
static constexpr const char *TEXT_DATA_ATTR = "text";
|
||||
// TextConfiguration::EmbossStyle
|
||||
static constexpr const char *STYLE_NAME_ATTR = "style_name";
|
||||
@ -3628,8 +3628,8 @@ std::optional<TextConfiguration> TextConfigurationSerialization::read(const char
|
||||
float distance = get_attribute_value_float(attributes, num_attributes, DISTANCE_ATTR);
|
||||
if (std::fabs(distance) > std::numeric_limits<float>::epsilon())
|
||||
fp.distance = distance;
|
||||
std::string use_surface = get_attribute_value_string(attributes, num_attributes, USE_SURFACE_ATTR);
|
||||
if (!use_surface.empty()) fp.use_surface = true;
|
||||
int use_surface = get_attribute_value_int(attributes, num_attributes, USE_SURFACE_ATTR);
|
||||
if (use_surface == 1) fp.use_surface = true;
|
||||
float angle = get_attribute_value_float(attributes, num_attributes, ANGLE_ATTR);
|
||||
if (std::fabs(angle) > std::numeric_limits<float>::epsilon())
|
||||
fp.angle = angle;
|
||||
|
@ -2327,7 +2327,7 @@ void GCode::process_layer_single_object(
|
||||
interface_extruder = dontcare_extruder;
|
||||
}
|
||||
bool extrude_support = has_support && support_extruder == extruder_id;
|
||||
bool extrude_interface = interface_extruder && interface_extruder == extruder_id;
|
||||
bool extrude_interface = has_interface && interface_extruder == extruder_id;
|
||||
if (extrude_support || extrude_interface) {
|
||||
init_layer_delayed();
|
||||
m_layer = layer_to_print.support_layer;
|
||||
|
@ -133,7 +133,7 @@ Slic3r::Pointfs compute_intersections(const Slic3r::Lines &lines)
|
||||
Point max_(std::max(a_.x(), b_.x()), std::max(a_.y(), b_.y()));
|
||||
BoundingBox bb_(min_, max_);
|
||||
// intersect of BB compare min max
|
||||
if (bb.intersects(bb_) &&
|
||||
if (bb.overlap(bb_) &&
|
||||
l.intersection(l_, &i))
|
||||
pts.push_back(i.cast<double>());
|
||||
}
|
||||
|
@ -317,15 +317,18 @@ void Layer::build_up_down_graph(Layer& below, Layer& above)
|
||||
coord_t* end = srcs + 4;
|
||||
std::sort(begin, end);
|
||||
end = std::unique(begin, end);
|
||||
assert(begin + 2 == end);
|
||||
if (begin + 1 == end)
|
||||
if (begin + 1 == end) {
|
||||
// Self intersection may happen on source contour. Just copy the Z value.
|
||||
pt.z() = *begin;
|
||||
else if (begin + 2 <= end) {
|
||||
} else {
|
||||
assert(begin + 2 == end);
|
||||
if (begin + 2 <= end) {
|
||||
// store a -1 based negative index into the "intersections" vector here.
|
||||
m_intersections.emplace_back(srcs[0], srcs[1]);
|
||||
pt.z() = -coord_t(m_intersections.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
const std::vector<std::pair<coord_t, coord_t>>& intersections() const { return m_intersections; }
|
||||
|
||||
private:
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include "Arrange.hpp"
|
||||
#include "CustomGCode.hpp"
|
||||
#include "enum_bitmask.hpp"
|
||||
//#include "ModelVolumeType.hpp"
|
||||
#include "TextConfiguration.hpp"
|
||||
|
||||
#include <map>
|
||||
|
@ -1,16 +0,0 @@
|
||||
#ifndef slic3r_ModelVolumeType_hpp_
|
||||
#define slic3r_ModelVolumeType_hpp_
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
enum class ModelVolumeType : int {
|
||||
INVALID = -1,
|
||||
MODEL_PART = 0,
|
||||
NEGATIVE_VOLUME,
|
||||
PARAMETER_MODIFIER,
|
||||
SUPPORT_BLOCKER,
|
||||
SUPPORT_ENFORCER,
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
#endif /* slic3r_ModelVolumeType_hpp_ */
|
@ -3,6 +3,8 @@
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
// inspired by nanosvgrast.h function nsvgRasterize -> nsvg__flattenShape -> nsvg__flattenCubicBez
|
||||
// https://github.com/memononen/nanosvg/blob/f0a3e1034dd22e2e87e5db22401e44998383124e/src/nanosvgrast.h#L335
|
||||
void NSVGUtils::flatten_cubic_bez(Polygon &polygon,
|
||||
float tessTol,
|
||||
Vec2f p1,
|
||||
|
@ -397,22 +397,37 @@ static ClipperLib_Z::Paths clip_extrusion(const ClipperLib_Z::Path &subject, con
|
||||
ClipperLib_Z::Clipper clipper;
|
||||
clipper.ZFillFunction([](const ClipperLib_Z::IntPoint &e1bot, const ClipperLib_Z::IntPoint &e1top, const ClipperLib_Z::IntPoint &e2bot,
|
||||
const ClipperLib_Z::IntPoint &e2top, ClipperLib_Z::IntPoint &pt) {
|
||||
// The clipping contour may be simplified by clipping it with a bounding box of "subject" path.
|
||||
// The clipping function used may produce self intersections outside of the "subject" bounding box. Such self intersections are
|
||||
// harmless to the result of the clipping operation,
|
||||
// Both ends of each edge belong to the same source: Either they are from subject or from clipping path.
|
||||
assert(e1bot.z() >= 0 && e1top.z() >= 0);
|
||||
assert(e2bot.z() >= 0 && e2top.z() >= 0);
|
||||
assert((e1bot.z() == 0) == (e1top.z() == 0));
|
||||
assert((e2bot.z() == 0) == (e2top.z() == 0));
|
||||
|
||||
// Start & end points of the clipped polyline (extrusion path with a non-zero width).
|
||||
ClipperLib_Z::IntPoint start = e1bot;
|
||||
ClipperLib_Z::IntPoint end = e1top;
|
||||
|
||||
if (start.z() <= 0 && end.z() <= 0) {
|
||||
start = e2bot;
|
||||
end = e2top;
|
||||
}
|
||||
|
||||
if (start.z() <= 0 && end.z() <= 0) {
|
||||
// Self intersection on the source contour.
|
||||
assert(start.z() == 0 && end.z() == 0);
|
||||
pt.z() = 0;
|
||||
} else {
|
||||
// Interpolate extrusion line width.
|
||||
assert(start.z() > 0 && end.z() > 0);
|
||||
|
||||
// Interpolate extrusion line width.
|
||||
double length_sqr = (end - start).cast<double>().squaredNorm();
|
||||
double dist_sqr = (pt - start).cast<double>().squaredNorm();
|
||||
double t = std::sqrt(dist_sqr / length_sqr);
|
||||
|
||||
pt.z() = start.z() + coord_t((end.z() - start.z()) * t);
|
||||
}
|
||||
});
|
||||
|
||||
clipper.AddPath(subject, ClipperLib_Z::ptSubject, false);
|
||||
|
@ -66,9 +66,9 @@ bool has_duplicate_points(std::vector<Point> &&pts)
|
||||
return false;
|
||||
}
|
||||
|
||||
Points collect_duplications(Points pts /* Copy */)
|
||||
Points collect_duplicates(Points pts /* Copy */)
|
||||
{
|
||||
std::stable_sort(pts.begin(), pts.end());
|
||||
std::sort(pts.begin(), pts.end());
|
||||
Points duplicits;
|
||||
const Point *prev = &pts.front();
|
||||
for (size_t i = 1; i < pts.size(); ++i) {
|
||||
|
@ -268,7 +268,7 @@ inline bool has_duplicate_successive_points_closed(const std::vector<Point> &pts
|
||||
}
|
||||
|
||||
// Collect adjecent(duplicit points)
|
||||
Points collect_duplications(Points pts /* Copy */);
|
||||
Points collect_duplicates(Points pts /* Copy */);
|
||||
|
||||
inline bool shorter_then(const Point& p0, const coord_t len)
|
||||
{
|
||||
|
@ -977,8 +977,10 @@ indexed_triangle_set its_make_cone(double r, double h, double fa)
|
||||
vertices.emplace_back(Vec3f(0., 0., h));
|
||||
|
||||
size_t i = 0;
|
||||
const auto vec = Eigen::Vector2f(0, float(r));
|
||||
for (double angle=0; angle<2*PI; angle+=fa) {
|
||||
vertices.emplace_back(r*std::cos(angle), r*std::sin(angle), 0.);
|
||||
Vec2f p = Eigen::Rotation2Df(angle) * vec;
|
||||
vertices.emplace_back(Vec3f(p(0), p(1), 0.f));
|
||||
if (angle > 0.) {
|
||||
facets.emplace_back(0, i+2, i+1);
|
||||
facets.emplace_back(1, i+1, i+2);
|
||||
@ -1013,58 +1015,121 @@ indexed_triangle_set its_make_pyramid(float base, float height)
|
||||
// Generates mesh for a sphere centered about the origin, using the generated angle
|
||||
// to determine the granularity.
|
||||
// Default angle is 1 degree.
|
||||
//FIXME better to discretize an Icosahedron recursively http://www.songho.ca/opengl/gl_sphere.html
|
||||
indexed_triangle_set its_make_sphere(double radius, double fa)
|
||||
{
|
||||
int sectorCount = int(ceil(2. * M_PI / fa));
|
||||
int stackCount = int(ceil(M_PI / fa));
|
||||
float sectorStep = float(2. * M_PI / sectorCount);
|
||||
float stackStep = float(M_PI / stackCount);
|
||||
|
||||
// First build an icosahedron (taken from http://www.songho.ca/opengl/gl_sphere.html)
|
||||
indexed_triangle_set mesh;
|
||||
|
||||
const float PI = 3.1415926f;
|
||||
const float H_ANGLE = PI / 180 * 72; // 72 degree = 360 / 5
|
||||
const float V_ANGLE = atanf(1.0f / 2); // elevation = 26.565 degree
|
||||
|
||||
auto& vertices = mesh.vertices;
|
||||
vertices.reserve((stackCount - 1) * sectorCount + 2);
|
||||
for (int i = 0; i <= stackCount; ++ i) {
|
||||
// from pi/2 to -pi/2
|
||||
double stackAngle = 0.5 * M_PI - stackStep * i;
|
||||
double xy = radius * cos(stackAngle);
|
||||
double z = radius * sin(stackAngle);
|
||||
if (i == 0 || i == stackCount)
|
||||
vertices.emplace_back(Vec3f(float(xy), 0.f, float(z)));
|
||||
else
|
||||
for (int j = 0; j < sectorCount; ++ j) {
|
||||
// from 0 to 2pi
|
||||
double sectorAngle = sectorStep * j;
|
||||
vertices.emplace_back(Vec3d(xy * std::cos(sectorAngle), xy * std::sin(sectorAngle), z).cast<float>());
|
||||
auto& indices = mesh.indices;
|
||||
vertices.resize(12);
|
||||
indices.reserve(20);
|
||||
|
||||
float z, xy;
|
||||
float hAngle1 = -PI / 2 - H_ANGLE / 2;
|
||||
|
||||
vertices[0] = stl_vertex(0, 0, radius); // the first top vertex at (0, 0, r)
|
||||
|
||||
for (int i = 1; i <= 5; ++i) {
|
||||
z = radius * sinf(V_ANGLE);
|
||||
xy = radius * cosf(V_ANGLE);
|
||||
vertices[i] = stl_vertex(xy * cosf(hAngle1), xy * sinf(hAngle1), z);
|
||||
vertices[i+5] = stl_vertex(xy * cosf(hAngle1 + H_ANGLE / 2), xy * sinf(hAngle1 + H_ANGLE / 2), -z);
|
||||
hAngle1 += H_ANGLE;
|
||||
|
||||
indices.emplace_back(stl_triangle_vertex_indices(i, i < 5 ? i+1 : 1, 0));
|
||||
indices.emplace_back(stl_triangle_vertex_indices(i, i+5, i < 5 ? i+1 : 1));
|
||||
indices.emplace_back(stl_triangle_vertex_indices(i+5, i+6 < 11 ? i+6 : 6, i+6 < 11 ? i+1 : 1));
|
||||
indices.emplace_back(stl_triangle_vertex_indices(i+5, 11, i+6 < 11 ? i+6 : 6));
|
||||
}
|
||||
vertices[11] = stl_vertex(0, 0, -radius); // the last bottom vertex at (0, 0, -r)
|
||||
|
||||
|
||||
// We have a beautiful icosahedron. Now subdivide the triangles.
|
||||
std::vector<Vec3i> neighbors = its_face_neighbors(mesh); // This is cheap, the mesh is small.
|
||||
|
||||
const double side_len_limit = radius * fa;
|
||||
const double side_len = (vertices[1] - vertices[0]).norm();
|
||||
const int iterations = std::ceil(std::log2(side_len / side_len_limit));
|
||||
|
||||
indices.reserve(indices.size() * std::pow(4, iterations));
|
||||
vertices.reserve(vertices.size() * std::pow(2, iterations));
|
||||
|
||||
struct DividedEdge {
|
||||
int neighbor = -1;
|
||||
int middle_vertex_idx;
|
||||
std::pair<int, int> children_idxs;
|
||||
};
|
||||
|
||||
for (int iter=0; iter<iterations; ++iter) {
|
||||
std::vector<std::array<DividedEdge, 3>> divided_triangles(indices.size());
|
||||
std::vector<Vec3i> new_neighbors(4*indices.size());
|
||||
|
||||
size_t orig_indices_size = indices.size();
|
||||
for (int i=0; i<orig_indices_size; ++i) { // iterate over all old triangles
|
||||
|
||||
// We are going to split this triangle. Let's foresee what will be the indices
|
||||
// of the new internal triangles along individual edges.
|
||||
int last_triangle_idx = indices.size()-1;
|
||||
std::array<std::pair<int, int>, 3> edge_children = { std::make_pair(i,last_triangle_idx + 2),
|
||||
std::make_pair(last_triangle_idx + 2,last_triangle_idx + 3),
|
||||
std::make_pair(last_triangle_idx + 3,i) };
|
||||
|
||||
std::array<int, 3> middle_vertices_idxs;
|
||||
std::array<std::pair<int, int>, 3> new_neighbors_per_edge;
|
||||
|
||||
for (int n=0; n<3; ++n) { // for all three edges
|
||||
const int edge_neighbor = neighbors[i][n];
|
||||
|
||||
if (divided_triangles[edge_neighbor][0].neighbor == -1) {
|
||||
// This n-th edge is not yet divided. Divide it now.
|
||||
vertices.emplace_back(0.5 * (vertices[indices[i][n]] + vertices[indices[i][n == 2 ? 0 : n+1]]));
|
||||
vertices.back() *= radius / vertices.back().norm();
|
||||
middle_vertices_idxs[n] = vertices.size()-1;
|
||||
|
||||
// Save information about what we did.
|
||||
int j = -1;
|
||||
while (divided_triangles[i][++j].neighbor != -1);
|
||||
|
||||
divided_triangles[i][j] = { edge_neighbor, int(vertices.size()-1), edge_children[n] };
|
||||
new_neighbors_per_edge[n] = std::make_pair(-1,-1);
|
||||
} else {
|
||||
// This edge is already divided. Get the index of the middle point.
|
||||
int j = -1;
|
||||
while (divided_triangles[edge_neighbor][++j].neighbor != i);
|
||||
middle_vertices_idxs[n] = divided_triangles[edge_neighbor][j].middle_vertex_idx;
|
||||
new_neighbors_per_edge[n] = divided_triangles[edge_neighbor][j].children_idxs;
|
||||
std::swap(new_neighbors_per_edge[n].first, new_neighbors_per_edge[n].second);
|
||||
|
||||
// We have saved the middle-point. We are looking for edges leading to/from it.
|
||||
int idx = -1; while (indices[new_neighbors_per_edge[n].first][++idx] != middle_vertices_idxs[n]);
|
||||
new_neighbors[new_neighbors_per_edge[n].first][idx] = edge_children[n].first;
|
||||
new_neighbors[new_neighbors_per_edge[n].second][idx] = edge_children[n].second;
|
||||
}
|
||||
}
|
||||
|
||||
auto& facets = mesh.indices;
|
||||
facets.reserve(2 * (stackCount - 1) * sectorCount);
|
||||
for (int i = 0; i < stackCount; ++ i) {
|
||||
// Beginning of current stack.
|
||||
int k1 = (i == 0) ? 0 : (1 + (i - 1) * sectorCount);
|
||||
int k1_first = k1;
|
||||
// Beginning of next stack.
|
||||
int k2 = (i == 0) ? 1 : (k1 + sectorCount);
|
||||
int k2_first = k2;
|
||||
for (int j = 0; j < sectorCount; ++ j) {
|
||||
// 2 triangles per sector excluding first and last stacks
|
||||
int k1_next = k1;
|
||||
int k2_next = k2;
|
||||
if (i != 0) {
|
||||
k1_next = (j + 1 == sectorCount) ? k1_first : (k1 + 1);
|
||||
facets.emplace_back(k1, k2, k1_next);
|
||||
}
|
||||
if (i + 1 != stackCount) {
|
||||
k2_next = (j + 1 == sectorCount) ? k2_first : (k2 + 1);
|
||||
facets.emplace_back(k1_next, k2, k2_next);
|
||||
}
|
||||
k1 = k1_next;
|
||||
k2 = k2_next;
|
||||
}
|
||||
}
|
||||
// Add three new triangles, reindex the old one.
|
||||
const int last_index = indices.size() - 1;
|
||||
indices.emplace_back(stl_triangle_vertex_indices(middle_vertices_idxs[0], middle_vertices_idxs[1], middle_vertices_idxs[2]));
|
||||
new_neighbors[indices.size()-1] = Vec3i(last_index+2, last_index+3, i);
|
||||
|
||||
indices.emplace_back(stl_triangle_vertex_indices(middle_vertices_idxs[0], indices[i][1], middle_vertices_idxs[1]));
|
||||
new_neighbors[indices.size()-1] = Vec3i(new_neighbors_per_edge[0].second, new_neighbors_per_edge[1].first, last_index+1);
|
||||
|
||||
indices.emplace_back(stl_triangle_vertex_indices(middle_vertices_idxs[2], middle_vertices_idxs[1], indices[i][2]));
|
||||
new_neighbors[indices.size()-1] = Vec3i(last_index+1, new_neighbors_per_edge[1].second, new_neighbors_per_edge[2].first);
|
||||
|
||||
indices[i][1] = middle_vertices_idxs[0];
|
||||
indices[i][2] = middle_vertices_idxs[2];
|
||||
new_neighbors[i] = Vec3i(new_neighbors_per_edge[0].first, last_index+1, new_neighbors_per_edge[2].second);
|
||||
|
||||
}
|
||||
neighbors = std::move(new_neighbors);
|
||||
}
|
||||
return mesh;
|
||||
}
|
||||
|
||||
|
@ -241,7 +241,7 @@ Triangulation::Indices Triangulation::triangulate(const ExPolygon &expolygon){
|
||||
|
||||
Triangulation::Indices Triangulation::triangulate(const ExPolygons &expolygons){
|
||||
Points pts = to_points(expolygons);
|
||||
Points d_pts = collect_duplications(pts);
|
||||
Points d_pts = collect_duplicates(pts);
|
||||
if (d_pts.empty()) return triangulate(expolygons, pts);
|
||||
|
||||
Changes changes = create_changes(pts, d_pts);
|
||||
@ -262,7 +262,7 @@ Triangulation::Indices Triangulation::triangulate(const ExPolygons &expolygons,
|
||||
{
|
||||
assert(count_points(expolygons) == points.size());
|
||||
// when contain duplicit coordinate in points will not work properly
|
||||
assert(collect_duplications(points).empty());
|
||||
assert(collect_duplicates(points).empty());
|
||||
|
||||
HalfEdges edges;
|
||||
edges.reserve(points.size());
|
||||
|
@ -113,7 +113,6 @@ inline void append(std::vector<T>& dest, const std::vector<T>& src)
|
||||
dest = src; // copy
|
||||
else
|
||||
dest.insert(dest.end(), src.begin(), src.end());
|
||||
// NOTE: insert reserve space when needed
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
@ -956,7 +956,8 @@ std::string xml_escape(std::string text, bool is_marked/* = false*/)
|
||||
}
|
||||
|
||||
// Definition of escape symbols https://www.w3.org/TR/REC-xml/#AVNormalize
|
||||
|
||||
// During the read of xml attribute normalization of white spaces is applied
|
||||
// Soo for not lose white space character it is escaped before store
|
||||
std::string xml_escape_double_quotes_attribute_value(std::string text)
|
||||
{
|
||||
std::string::size_type pos = 0;
|
||||
|
@ -448,11 +448,15 @@ int GLVolumeCollection::load_object_volume(
|
||||
this->volumes.emplace_back(new GLVolume());
|
||||
GLVolume& v = *this->volumes.back();
|
||||
v.set_color(color_from_model_volume(*model_volume));
|
||||
// apply printable value from the instance
|
||||
v.printable = instance->printable;
|
||||
#if ENABLE_SMOOTH_NORMALS
|
||||
v.model.init_from(*mesh, true);
|
||||
if (m_use_raycasters)
|
||||
v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(mesh);
|
||||
#else
|
||||
v.model.init_from(*mesh);
|
||||
if (m_use_raycasters)
|
||||
v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(mesh);
|
||||
#endif // ENABLE_SMOOTH_NORMALS
|
||||
v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx);
|
||||
|
@ -397,6 +397,7 @@ private:
|
||||
Slope m_slope;
|
||||
bool m_show_sinking_contours{ false };
|
||||
bool m_show_non_manifold_edges{ true };
|
||||
bool m_use_raycasters{ true };
|
||||
|
||||
public:
|
||||
GLVolumePtrs volumes;
|
||||
@ -445,6 +446,7 @@ public:
|
||||
bool empty() const { return volumes.empty(); }
|
||||
void set_range(double low, double high) { for (GLVolume* vol : this->volumes) vol->set_range(low, high); }
|
||||
|
||||
void set_use_raycasters(bool value) { m_use_raycasters = value; }
|
||||
void set_print_volume(const PrintVolume& print_volume) { m_print_volume = print_volume; }
|
||||
|
||||
void set_z_range(float min_z, float max_z) { m_z_range[0] = min_z; m_z_range[1] = max_z; }
|
||||
|
@ -1386,6 +1386,45 @@ void PageFirmware::apply_custom_config(DynamicPrintConfig &config)
|
||||
}
|
||||
}
|
||||
|
||||
static void focus_event(wxFocusEvent& e, wxTextCtrl* ctrl, double def_value)
|
||||
{
|
||||
e.Skip();
|
||||
wxString str = ctrl->GetValue();
|
||||
|
||||
const char dec_sep = is_decimal_separator_point() ? '.' : ',';
|
||||
const char dec_sep_alt = dec_sep == '.' ? ',' : '.';
|
||||
// Replace the first incorrect separator in decimal number.
|
||||
bool was_replaced = str.Replace(dec_sep_alt, dec_sep, false) != 0;
|
||||
|
||||
double val = 0.0;
|
||||
if (!str.ToDouble(&val)) {
|
||||
if (val == 0.0)
|
||||
val = def_value;
|
||||
ctrl->SetValue(double_to_string(val));
|
||||
show_error(nullptr, _L("Invalid numeric input."));
|
||||
// On Windows, this SetFocus creates an invisible marker.
|
||||
//ctrl->SetFocus();
|
||||
}
|
||||
else if (was_replaced)
|
||||
ctrl->SetValue(double_to_string(val));
|
||||
}
|
||||
|
||||
class DiamTextCtrl : public wxTextCtrl
|
||||
{
|
||||
public:
|
||||
DiamTextCtrl(wxWindow* parent)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
long style = wxBORDER_SIMPLE;
|
||||
#else
|
||||
long style = 0;
|
||||
#endif
|
||||
Create(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(Field::def_width_thinner() * wxGetApp().em_unit(), wxDefaultCoord), style);
|
||||
wxGetApp().UpdateDarkUI(this);
|
||||
}
|
||||
~DiamTextCtrl() {}
|
||||
};
|
||||
|
||||
PageBedShape::PageBedShape(ConfigWizard *parent)
|
||||
: ConfigWizardPage(parent, _L("Bed Shape and Size"), _L("Bed Shape"), 1)
|
||||
, shape_panel(new BedShapePanel(this))
|
||||
@ -1409,10 +1448,20 @@ void PageBedShape::apply_custom_config(DynamicPrintConfig &config)
|
||||
config.set_key_value("bed_custom_model", new ConfigOptionString(custom_model));
|
||||
}
|
||||
|
||||
static void focus_event(wxFocusEvent& e, wxTextCtrl* ctrl, double def_value)
|
||||
PageBuildVolume::PageBuildVolume(ConfigWizard* parent)
|
||||
: ConfigWizardPage(parent, _L("Build Volume"), _L("Build Volume"), 1)
|
||||
, build_volume(new DiamTextCtrl(this))
|
||||
{
|
||||
append_text(_L("Set verctical size of your printer."));
|
||||
|
||||
wxString value = "200";
|
||||
build_volume->SetValue(value);
|
||||
|
||||
build_volume->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& e) {
|
||||
double def_value = 200.0;
|
||||
double max_value = 1200.0;
|
||||
e.Skip();
|
||||
wxString str = ctrl->GetValue();
|
||||
wxString str = build_volume->GetValue();
|
||||
|
||||
const char dec_sep = is_decimal_separator_point() ? '.' : ',';
|
||||
const char dec_sep_alt = dec_sep == '.' ? ',' : '.';
|
||||
@ -1421,31 +1470,41 @@ static void focus_event(wxFocusEvent& e, wxTextCtrl* ctrl, double def_value)
|
||||
|
||||
double val = 0.0;
|
||||
if (!str.ToDouble(&val)) {
|
||||
if (val == 0.0)
|
||||
val = def_value;
|
||||
ctrl->SetValue(double_to_string(val));
|
||||
build_volume->SetValue(double_to_string(val));
|
||||
show_error(nullptr, _L("Invalid numeric input."));
|
||||
ctrl->SetFocus();
|
||||
}
|
||||
else if (was_replaced)
|
||||
ctrl->SetValue(double_to_string(val));
|
||||
//build_volume->SetFocus();
|
||||
} else if (val < 0.0) {
|
||||
val = def_value;
|
||||
build_volume->SetValue(double_to_string(val));
|
||||
show_error(nullptr, _L("Invalid numeric input."));
|
||||
//build_volume->SetFocus();
|
||||
} else if (val > max_value) {
|
||||
val = max_value;
|
||||
build_volume->SetValue(double_to_string(val));
|
||||
show_error(nullptr, _L("Invalid numeric input."));
|
||||
//build_volume->SetFocus();
|
||||
} else if (was_replaced)
|
||||
build_volume->SetValue(double_to_string(val));
|
||||
}, build_volume->GetId());
|
||||
|
||||
auto* sizer_volume = new wxFlexGridSizer(3, 5, 5);
|
||||
auto* text_volume = new wxStaticText(this, wxID_ANY, _L("Max print height:"));
|
||||
auto* unit_volume = new wxStaticText(this, wxID_ANY, _L("mm"));
|
||||
sizer_volume->AddGrowableCol(0, 1);
|
||||
sizer_volume->Add(text_volume, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
sizer_volume->Add(build_volume);
|
||||
sizer_volume->Add(unit_volume, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
append(sizer_volume);
|
||||
}
|
||||
|
||||
class DiamTextCtrl : public wxTextCtrl
|
||||
void PageBuildVolume::apply_custom_config(DynamicPrintConfig& config)
|
||||
{
|
||||
public:
|
||||
DiamTextCtrl(wxWindow* parent)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
long style = wxBORDER_SIMPLE;
|
||||
#else
|
||||
long style = 0;
|
||||
#endif
|
||||
Create(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(Field::def_width_thinner() * wxGetApp().em_unit(), wxDefaultCoord), style);
|
||||
wxGetApp().UpdateDarkUI(this);
|
||||
double val = 0.0;
|
||||
build_volume->GetValue().ToDouble(&val);
|
||||
auto* opt_volume = new ConfigOptionFloat(val);
|
||||
config.set_key_value("max_print_height", opt_volume);
|
||||
}
|
||||
~DiamTextCtrl() {}
|
||||
};
|
||||
|
||||
PageDiameters::PageDiameters(ConfigWizard *parent)
|
||||
: ConfigWizardPage(parent, _L("Filament and Nozzle Diameters"), _L("Print Diameters"), 1)
|
||||
@ -1915,6 +1974,7 @@ void ConfigWizard::priv::load_pages()
|
||||
if (page_custom->custom_wanted()) {
|
||||
index->add_page(page_firmware);
|
||||
index->add_page(page_bed);
|
||||
index->add_page(page_bvolume);
|
||||
index->add_page(page_diams);
|
||||
index->add_page(page_temps);
|
||||
}
|
||||
@ -2773,6 +2833,7 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
||||
|
||||
page_firmware->apply_custom_config(*custom_config);
|
||||
page_bed->apply_custom_config(*custom_config);
|
||||
page_bvolume->apply_custom_config(*custom_config);
|
||||
page_diams->apply_custom_config(*custom_config);
|
||||
page_temps->apply_custom_config(*custom_config);
|
||||
|
||||
@ -2923,6 +2984,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
|
||||
p->add_page(p->page_mode = new PageMode(this));
|
||||
p->add_page(p->page_firmware = new PageFirmware(this));
|
||||
p->add_page(p->page_bed = new PageBedShape(this));
|
||||
p->add_page(p->page_bvolume = new PageBuildVolume(this));
|
||||
p->add_page(p->page_diams = new PageDiameters(this));
|
||||
p->add_page(p->page_temps = new PageTemperatures(this));
|
||||
|
||||
|
@ -450,6 +450,14 @@ struct PageBedShape: ConfigWizardPage
|
||||
virtual void apply_custom_config(DynamicPrintConfig &config);
|
||||
};
|
||||
|
||||
struct PageBuildVolume : ConfigWizardPage
|
||||
{
|
||||
wxTextCtrl* build_volume;
|
||||
|
||||
PageBuildVolume(ConfigWizard* parent);
|
||||
virtual void apply_custom_config(DynamicPrintConfig& config);
|
||||
};
|
||||
|
||||
struct PageDiameters: ConfigWizardPage
|
||||
{
|
||||
wxTextCtrl *diam_nozzle;
|
||||
@ -584,6 +592,7 @@ struct ConfigWizard::priv
|
||||
PageBedShape *page_bed = nullptr;
|
||||
PageDiameters *page_diams = nullptr;
|
||||
PageTemperatures *page_temps = nullptr;
|
||||
PageBuildVolume* page_bvolume = nullptr;
|
||||
|
||||
// Pointers to all pages (regardless or whether currently part of the ConfigWizardIndex)
|
||||
std::vector<ConfigWizardPage*> all_pages;
|
||||
|
@ -643,7 +643,7 @@ const ColorRGBA GCodeViewer::Neutral_Color = ColorRGBA::DARK_GRAY();
|
||||
GCodeViewer::GCodeViewer()
|
||||
{
|
||||
m_extrusions.reset_role_visibility_flags();
|
||||
|
||||
m_shells.volumes.set_use_raycasters(false);
|
||||
// m_sequential_view.skip_invisible_moves = true;
|
||||
}
|
||||
|
||||
|
@ -81,6 +81,11 @@ static const Slic3r::ColorRGBA ERROR_BG_LIGHT_COLOR = { 0.753f, 0.192f, 0.039f
|
||||
// Number of floats
|
||||
static constexpr const size_t MAX_VERTEX_BUFFER_SIZE = 131072 * 6; // 3.15MB
|
||||
|
||||
#define SHOW_IMGUI_DEMO_WINDOW
|
||||
#ifdef SHOW_IMGUI_DEMO_WINDOW
|
||||
static bool show_imgui_demo_window = false;
|
||||
#endif // SHOW_IMGUI_DEMO_WINDOW
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
@ -1499,6 +1504,10 @@ void GLCanvas3D::render()
|
||||
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
||||
}
|
||||
|
||||
#ifdef SHOW_IMGUI_DEMO_WINDOW
|
||||
if (show_imgui_demo_window) ImGui::ShowDemoWindow();
|
||||
#endif // SHOW_IMGUI_DEMO_WINDOW
|
||||
|
||||
const bool is_looking_downward = camera.is_looking_downward();
|
||||
|
||||
// draw scene
|
||||
@ -2379,10 +2388,11 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
||||
if (!m_initialized)
|
||||
return;
|
||||
|
||||
// see include/wx/defs.h enum wxKeyCode
|
||||
int keyCode = evt.GetKeyCode();
|
||||
int ctrlMask = wxMOD_CONTROL;
|
||||
int shiftMask = wxMOD_SHIFT;
|
||||
#ifdef SHOW_IMGUI_DEMO_WINDOW
|
||||
static int cur = 0;
|
||||
if (wxString("demo")[cur] == evt.GetUnicodeKey()) ++cur; else cur = 0;
|
||||
if (cur == 4) { show_imgui_demo_window = !show_imgui_demo_window; cur = 0;}
|
||||
#endif // SHOW_IMGUI_DEMO_WINDOW
|
||||
|
||||
auto imgui = wxGetApp().imgui();
|
||||
if (imgui->update_key_data(evt)) {
|
||||
@ -2390,6 +2400,10 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
||||
return;
|
||||
}
|
||||
|
||||
// see include/wx/defs.h enum wxKeyCode
|
||||
int keyCode = evt.GetKeyCode();
|
||||
int ctrlMask = wxMOD_CONTROL;
|
||||
int shiftMask = wxMOD_SHIFT;
|
||||
if (keyCode == WXK_ESCAPE && (_deactivate_undo_redo_toolbar_items() || _deactivate_search_toolbar_item() || _deactivate_arrange_menu()))
|
||||
return;
|
||||
|
||||
@ -3447,22 +3461,29 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
evt.Skip();
|
||||
|
||||
// Detection of doubleclick on text to open emboss edit window
|
||||
if (evt.LeftDClick() && m_gizmos.get_current() == nullptr && !m_hover_volume_idxs.empty()) {
|
||||
auto type = m_gizmos.get_current_type();
|
||||
if (evt.LeftDClick() && !m_hover_volume_idxs.empty() &&
|
||||
(type == GLGizmosManager::EType::Undefined ||
|
||||
type == GLGizmosManager::EType::Move ||
|
||||
type == GLGizmosManager::EType::Rotate ||
|
||||
type == GLGizmosManager::EType::Scale ||
|
||||
type == GLGizmosManager::EType::Emboss) ) {
|
||||
for (int hover_volume_id : m_hover_volume_idxs) {
|
||||
const GLVolume &hover_gl_volume = *m_volumes.volumes[hover_volume_id];
|
||||
const ModelObject* hover_object = m_model->objects[hover_gl_volume.object_idx()];
|
||||
int object_idx = hover_gl_volume.object_idx();
|
||||
if (object_idx < 0 || object_idx >= m_model->objects.size()) continue;
|
||||
const ModelObject* hover_object = m_model->objects[object_idx];
|
||||
int hover_volume_idx = hover_gl_volume.volume_idx();
|
||||
if (hover_volume_idx < 0 || hover_volume_idx >= hover_object->volumes.size()) continue;
|
||||
const ModelVolume* hover_volume = hover_object->volumes[hover_volume_idx];
|
||||
if (hover_volume->text_configuration.has_value()) {
|
||||
//m_selection.set_mode(Selection::EMode::Volume);
|
||||
//m_selection.add(hover_volume_id); // add whole instance
|
||||
if (!hover_volume->text_configuration.has_value()) continue;
|
||||
m_selection.add_volumes(Selection::EMode::Volume, {(unsigned) hover_volume_id});
|
||||
if (type != GLGizmosManager::EType::Emboss)
|
||||
m_gizmos.open_gizmo(GLGizmosManager::EType::Emboss);
|
||||
wxGetApp().obj_list()->update_selections();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_moving)
|
||||
show_sinking_contours();
|
||||
|
@ -970,16 +970,19 @@ void MenuFactory::append_menu_item_edit_text(wxMenu *menu)
|
||||
wxString name = _L("Edit text");
|
||||
|
||||
auto can_edit_text = []() {
|
||||
const auto& sel = plater()->get_selection();
|
||||
if (sel.volumes_count() != 1) return false;
|
||||
auto cid = sel.get_volume(*sel.get_volume_idxs().begin());
|
||||
const ModelVolume* vol = plater()->canvas3D()->get_model()
|
||||
->objects[cid->object_idx()]->volumes[cid->volume_idx()];
|
||||
if (plater() != nullptr) {
|
||||
const Selection& sel = plater()->get_selection();
|
||||
if (sel.volumes_count() == 1) {
|
||||
const GLVolume* gl_vol = sel.get_first_volume();
|
||||
const ModelVolume* vol = plater()->model().objects[gl_vol->object_idx()]->volumes[gl_vol->volume_idx()];
|
||||
return vol->text_configuration.has_value();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (menu == &m_object_menu) {
|
||||
auto menu_item_id = menu->FindItem(name);
|
||||
if (menu != &m_text_part_menu) {
|
||||
const int menu_item_id = menu->FindItem(name);
|
||||
if (menu_item_id != wxNOT_FOUND)
|
||||
menu->Destroy(menu_item_id);
|
||||
if (!can_edit_text())
|
||||
|
@ -30,6 +30,7 @@ static const ColorRGBA SELECTED_PLAG_COLOR = ColorRGBA::GRAY();
|
||||
static const ColorRGBA SELECTED_DOWEL_COLOR = ColorRGBA::DARK_GRAY();
|
||||
static const ColorRGBA CONNECTOR_DEF_COLOR = ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f);
|
||||
static const ColorRGBA CONNECTOR_ERR_COLOR = ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f);
|
||||
static const ColorRGBA HOVERED_ERR_COLOR = ColorRGBA(1.0f, 0.3f, 0.3f, 1.0f);
|
||||
|
||||
const unsigned int AngleResolution = 64;
|
||||
const unsigned int ScaleStepsCount = 72;
|
||||
@ -359,10 +360,19 @@ void GLGizmoCut3D::put_connectors_on_cut_plane(const Vec3d& cp_normal, double cp
|
||||
}
|
||||
}
|
||||
|
||||
// returns true if the camera (forward) is pointing in the negative direction of the cut normal
|
||||
bool GLGizmoCut3D::is_looking_forward() const
|
||||
{
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
const double dot = camera.get_dir_forward().dot(m_cut_normal);
|
||||
return dot < 0.05;
|
||||
}
|
||||
|
||||
void GLGizmoCut3D::update_clipper()
|
||||
{
|
||||
BoundingBoxf3 box = bounding_box();
|
||||
|
||||
// update cut_normal
|
||||
Vec3d beg, end = beg = m_plane_center;
|
||||
beg[Z] = box.center().z() - m_radius;
|
||||
end[Z] = box.center().z() + m_radius;
|
||||
@ -370,12 +380,26 @@ void GLGizmoCut3D::update_clipper()
|
||||
rotate_vec3d_around_plane_center(beg);
|
||||
rotate_vec3d_around_plane_center(end);
|
||||
|
||||
double dist = (m_plane_center - beg).norm();
|
||||
// calculate normal for cut plane
|
||||
Vec3d normal = m_cut_normal = end - beg;
|
||||
m_cut_normal.normalize();
|
||||
|
||||
// calculate normal and offset for clipping plane
|
||||
Vec3d normal = end - beg;
|
||||
if (!is_looking_forward()) {
|
||||
end = beg = m_plane_center;
|
||||
beg[Z] = box.center().z() + m_radius;
|
||||
end[Z] = box.center().z() - m_radius;
|
||||
|
||||
rotate_vec3d_around_plane_center(beg);
|
||||
rotate_vec3d_around_plane_center(end);
|
||||
|
||||
// recalculate normal for clipping plane, if camera is looking downward to cut plane
|
||||
normal = end - beg;
|
||||
if (normal == Vec3d::Zero())
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate normal and offset for clipping plane
|
||||
double dist = (m_plane_center - beg).norm();
|
||||
dist = std::clamp(dist, 0.0001, normal.norm());
|
||||
normal.normalize();
|
||||
const double offset = normal.dot(beg) + dist;
|
||||
@ -1372,7 +1396,7 @@ void GLGizmoCut3D::render_clipper_cut()
|
||||
|
||||
void GLGizmoCut3D::on_render()
|
||||
{
|
||||
if (update_bb() || force_update_clipper_on_render) {
|
||||
if (update_bb() || force_update_clipper_on_render || m_connectors_editing) {
|
||||
update_clipper_on_render();
|
||||
m_c->object_clipper()->set_behavior(m_connectors_editing, m_connectors_editing, 0.4);
|
||||
}
|
||||
@ -1826,7 +1850,7 @@ Transform3d GLGizmoCut3D::get_volume_transformation(const ModelVolume* volume) c
|
||||
return translation_transform(offset) * scale_transform(Vec3d::Ones() - border_scale) * vol_matrix;
|
||||
}
|
||||
|
||||
bool GLGizmoCut3D::is_conflict_for_connector(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos)
|
||||
bool GLGizmoCut3D::is_outside_of_cut_contour(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos)
|
||||
{
|
||||
// check if connector pos is out of clipping plane
|
||||
if (m_c->object_clipper() && !m_c->object_clipper()->is_projection_inside_cut(cur_pos)) {
|
||||
@ -1834,16 +1858,54 @@ bool GLGizmoCut3D::is_conflict_for_connector(size_t idx, const CutConnectors& co
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if connector bottom contour is out of clipping plane
|
||||
const CutConnector& cur_connector = connectors[idx];
|
||||
const CutConnectorShape shape = CutConnectorShape(cur_connector.attribs.shape);
|
||||
const int sectorCount = shape == CutConnectorShape::Triangle ? 3 :
|
||||
shape == CutConnectorShape::Square ? 4 :
|
||||
shape == CutConnectorShape::Circle ? 60: // supposably, 60 points are enough for conflict detection
|
||||
shape == CutConnectorShape::Hexagon ? 6 : 1 ;
|
||||
|
||||
indexed_triangle_set mesh;
|
||||
auto& vertices = mesh.vertices;
|
||||
vertices.reserve(sectorCount + 1);
|
||||
|
||||
float fa = 2 * PI / sectorCount;
|
||||
auto vec = Eigen::Vector2f(0, cur_connector.radius);
|
||||
for (float angle = 0; angle < 2.f * PI; angle += fa) {
|
||||
Vec2f p = Eigen::Rotation2Df(angle) * vec;
|
||||
vertices.emplace_back(Vec3f(p(0), p(1), 0.f));
|
||||
}
|
||||
its_transform(mesh, translation_transform(cur_pos) * m_rotation_m);
|
||||
|
||||
for (auto vertex : vertices) {
|
||||
if (m_c->object_clipper() && !m_c->object_clipper()->is_projection_inside_cut(vertex.cast<double>())) {
|
||||
m_info_stats.outside_cut_contour++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLGizmoCut3D::is_conflict_for_connector(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos)
|
||||
{
|
||||
if (is_outside_of_cut_contour(idx, connectors, cur_pos))
|
||||
return true;
|
||||
|
||||
const CutConnector& cur_connector = connectors[idx];
|
||||
|
||||
const Transform3d matrix = translation_transform(cur_pos) * m_rotation_m *
|
||||
scale_transform(Vec3f(cur_connector.radius, cur_connector.radius, cur_connector.height).cast<double>());
|
||||
const BoundingBoxf3 cur_tbb = m_shapes[cur_connector.attribs].model.get_bounding_box().transformed(matrix);
|
||||
|
||||
// check if connector's bounding box is inside the object's bounding box
|
||||
if (!bounding_box().contains(cur_tbb)) {
|
||||
m_info_stats.outside_bb++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if connectors are overlapping
|
||||
for (size_t i = 0; i < connectors.size(); ++i) {
|
||||
if (i == idx)
|
||||
continue;
|
||||
@ -1897,7 +1959,8 @@ void GLGizmoCut3D::render_connectors()
|
||||
Vec3d pos = connector.pos + instance_offset + sla_shift * Vec3d::UnitZ();
|
||||
|
||||
// First decide about the color of the point.
|
||||
if (is_conflict_for_connector(i, connectors, pos)) {
|
||||
const bool conflict_connector = is_conflict_for_connector(i, connectors, pos);
|
||||
if (conflict_connector) {
|
||||
m_has_invalid_connector = true;
|
||||
render_color = CONNECTOR_ERR_COLOR;
|
||||
}
|
||||
@ -1907,16 +1970,23 @@ void GLGizmoCut3D::render_connectors()
|
||||
if (!m_connectors_editing)
|
||||
render_color = CONNECTOR_ERR_COLOR;
|
||||
else if (size_t(m_hover_id - m_connectors_group_id) == i)
|
||||
render_color = connector.attribs.type == CutConnectorType::Dowel ? HOVERED_DOWEL_COLOR : HOVERED_PLAG_COLOR;
|
||||
render_color = conflict_connector ? HOVERED_ERR_COLOR :
|
||||
connector.attribs.type == CutConnectorType::Dowel ? HOVERED_DOWEL_COLOR : HOVERED_PLAG_COLOR;
|
||||
else if (m_selected[i])
|
||||
render_color = connector.attribs.type == CutConnectorType::Dowel ? SELECTED_DOWEL_COLOR : SELECTED_PLAG_COLOR;
|
||||
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
if (connector.attribs.type == CutConnectorType::Dowel &&
|
||||
connector.attribs.style == CutConnectorStyle::Prizm) {
|
||||
if (is_looking_forward())
|
||||
pos -= height * normal;
|
||||
else
|
||||
pos += height * normal;
|
||||
height *= 2;
|
||||
}
|
||||
else if (!is_looking_forward())
|
||||
pos += 0.05 * normal;
|
||||
|
||||
const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(pos) * m_rotation_m *
|
||||
scale_transform(Vec3f(connector.radius, connector.radius, height).cast<double>());
|
||||
|
||||
|
@ -73,6 +73,7 @@ class GLGizmoCut3D : public GLGizmoBase
|
||||
GLModel m_angle_arc;
|
||||
|
||||
Vec3d m_old_center;
|
||||
Vec3d m_cut_normal;
|
||||
|
||||
struct InvalidConnectorsStatistics
|
||||
{
|
||||
@ -160,6 +161,7 @@ public:
|
||||
|
||||
bool is_in_editing_mode() const override { return m_connectors_editing; }
|
||||
bool is_selection_rectangle_dragging() const override { return m_selection_rectangle.is_dragging(); }
|
||||
bool is_looking_forward() const;
|
||||
|
||||
/// <summary>
|
||||
/// Drag of plane
|
||||
@ -239,6 +241,7 @@ private:
|
||||
bool render_reset_button(const std::string& label_id, const std::string& tooltip) const;
|
||||
bool render_connect_type_radio_button(CutConnectorType type);
|
||||
Transform3d get_volume_transformation(const ModelVolume* volume) const;
|
||||
bool is_outside_of_cut_contour(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos);
|
||||
bool is_conflict_for_connector(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos);
|
||||
void render_connectors();
|
||||
|
||||
|
@ -54,7 +54,8 @@
|
||||
#define SHOW_ICONS_TEXTURE
|
||||
#define SHOW_FINE_POSITION // draw convex hull around volume
|
||||
#define SHOW_WX_WEIGHT_INPUT
|
||||
#define DRAW_PLACE_TO_ADD_TEXT
|
||||
#define DRAW_PLACE_TO_ADD_TEXT // Interactive draw of window position
|
||||
#define ALLOW_FLOAT_WINDOW
|
||||
#endif // ALLOW_DEBUG_MODE
|
||||
|
||||
using namespace Slic3r;
|
||||
@ -204,23 +205,6 @@ static void find_closest_volume(const Selection &selection,
|
||||
/// <param name="emboss_data">Define params of text</param>
|
||||
/// <param name="coor">Screen coordinat, where to create new object laying on bed</param>
|
||||
static void start_create_object_job(DataBase &emboss_data, const Vec2d &coor);
|
||||
|
||||
static void message_disable_cut_surface(){
|
||||
wxMessageBox(_L("Can NOT cut surface from nothing. Function 'use surface' was disabled for this text."),
|
||||
_L("Disable 'use surface' from style"), wxOK | wxICON_WARNING);}
|
||||
|
||||
/// <summary>
|
||||
/// Create transformation for new created emboss object by mouse position
|
||||
/// </summary>
|
||||
/// <param name="screen_coor">Define where to add object</param>
|
||||
/// <param name="camera">Actual camera view</param>
|
||||
/// <param name="bed_shape">Define shape of bed for its center and check that coor is on bed center</param>
|
||||
/// <param name="z">Emboss size / 2</param>
|
||||
/// <returns>Transformation for create text on bed</returns>
|
||||
static Transform3d create_transformation_on_bed(const Vec2d &screen_coor,
|
||||
const Camera &camera,
|
||||
const std::vector<Vec2d> &bed_shape,
|
||||
double z);
|
||||
} // namespace priv
|
||||
|
||||
bool priv::is_valid(ModelVolumeType volume_type){
|
||||
@ -319,8 +303,8 @@ bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event)
|
||||
angle_opt.reset();
|
||||
|
||||
// set into activ style
|
||||
assert(m_style_manager.is_activ_font());
|
||||
if (m_style_manager.is_activ_font())
|
||||
assert(m_style_manager.is_active_font());
|
||||
if (m_style_manager.is_active_font())
|
||||
m_style_manager.get_font_prop().angle = angle_opt;
|
||||
|
||||
}
|
||||
@ -329,6 +313,15 @@ bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event)
|
||||
|
||||
namespace priv {
|
||||
|
||||
/// <summary>
|
||||
/// Access to model from gl_volume
|
||||
/// TODO: it is more general function --> move to utils
|
||||
/// </summary>
|
||||
/// <param name="gl_volume">Volume to model belongs to</param>
|
||||
/// <param name="object">Object containing gl_volume</param>
|
||||
/// <returns>Model for volume</returns>
|
||||
static ModelVolume *get_model_volume(const GLVolume *gl_volume, const ModelObject *object);
|
||||
|
||||
/// <summary>
|
||||
/// Access to model from gl_volume
|
||||
/// TODO: it is more general function --> move to utils
|
||||
@ -343,9 +336,8 @@ static ModelVolume *get_model_volume(const GLVolume *gl_volume, const ModelObjec
|
||||
/// TODO: it is more general function --> move to select utils
|
||||
/// </summary>
|
||||
/// <param name="selection">Actual selection</param>
|
||||
/// <param name="objects">All objects</param>
|
||||
/// <returns>Model from selection</returns>
|
||||
static ModelVolume *get_selected_volume(const Selection &selection, const ModelObjectPtrs &objects);
|
||||
static ModelVolume *get_selected_volume(const Selection &selection);
|
||||
|
||||
/// <summary>
|
||||
/// Calculate offset from mouse position to center of text
|
||||
@ -355,8 +347,62 @@ static ModelVolume *get_selected_volume(const Selection &selection, const ModelO
|
||||
/// <returns>Offset in screan coordinate</returns>
|
||||
static Vec2d calc_mouse_to_center_text_offset(const Vec2d &mouse, const ModelVolume &mv);
|
||||
|
||||
/// <summary>
|
||||
/// Access to one selected volume
|
||||
/// </summary>
|
||||
/// <param name="selection">Containe what is selected</param>
|
||||
/// <returns>Slected when only one volume otherwise nullptr</returns>
|
||||
static const GLVolume *get_gl_volume(const Selection &selection);
|
||||
|
||||
/// <summary>
|
||||
/// Get transformation to world
|
||||
/// - use fix after store to 3mf when exists
|
||||
/// </summary>
|
||||
/// <param name="gl_volume"></param>
|
||||
/// <param name="model">To identify MovelVolume with fix transformation</param>
|
||||
/// <returns></returns>
|
||||
static Transform3d world_matrix(const GLVolume *gl_volume, const Model *model);
|
||||
static Transform3d world_matrix(const Selection &selection);
|
||||
|
||||
} // namespace priv
|
||||
|
||||
const GLVolume *priv::get_gl_volume(const Selection &selection) {
|
||||
const auto &list = selection.get_volume_idxs();
|
||||
if (list.size() != 1)
|
||||
return nullptr;
|
||||
unsigned int volume_idx = *list.begin();
|
||||
return selection.get_volume(volume_idx);
|
||||
}
|
||||
|
||||
Transform3d priv::world_matrix(const GLVolume *gl_volume, const Model *model)
|
||||
{
|
||||
if (!gl_volume)
|
||||
return Transform3d::Identity();
|
||||
Transform3d res = gl_volume->world_matrix();
|
||||
|
||||
if (!model)
|
||||
return res;
|
||||
ModelVolume* mv = get_model_volume(gl_volume, model->objects);
|
||||
if (!mv)
|
||||
return res;
|
||||
|
||||
const std::optional<TextConfiguration> &tc = mv->text_configuration;
|
||||
if (!tc.has_value())
|
||||
return res;
|
||||
|
||||
const std::optional<Transform3d> &fix = tc->fix_3mf_tr;
|
||||
if (!fix.has_value())
|
||||
return res;
|
||||
|
||||
return res * (*fix);
|
||||
}
|
||||
|
||||
Transform3d priv::world_matrix(const Selection &selection)
|
||||
{
|
||||
const GLVolume *gl_volume = get_gl_volume(selection);
|
||||
return world_matrix(gl_volume, selection.get_model());
|
||||
}
|
||||
|
||||
Vec2d priv::calc_mouse_to_center_text_offset(const Vec2d& mouse, const ModelVolume& mv) {
|
||||
const Transform3d &volume_tr = mv.get_matrix();
|
||||
const Camera &camera = wxGetApp().plater()->get_camera();
|
||||
@ -522,6 +568,7 @@ bool GLGizmoEmboss::on_init()
|
||||
m_rotate_gizmo.init();
|
||||
ColorRGBA gray_color(.6f, .6f, .6f, .3f);
|
||||
m_rotate_gizmo.set_highlight_color(gray_color);
|
||||
m_shortcut_key = WXK_CONTROL_T;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -639,11 +686,27 @@ static void draw_mouse_offset(const std::optional<Vec2d> &offset)
|
||||
draw_list->AddLine(p1, p2, color, thickness);
|
||||
}
|
||||
#endif // SHOW_OFFSET_DURING_DRAGGING
|
||||
namespace priv {
|
||||
static void draw_origin_ball(const GLCanvas3D& canvas) {
|
||||
auto draw_list = ImGui::GetOverlayDrawList();
|
||||
const Selection &selection = canvas.get_selection();
|
||||
Transform3d to_world = priv::world_matrix(selection);
|
||||
Vec3d volume_zero = to_world * Vec3d::Zero();
|
||||
|
||||
const Camera &camera = wxGetApp().plater()->get_camera();
|
||||
Point screen_coor = CameraUtils::project(camera, volume_zero);
|
||||
ImVec2 center(screen_coor.x(), screen_coor.y());
|
||||
float radius = 10.f;
|
||||
ImU32 color = ImGui::GetColorU32(ImGuiWrapper::COL_ORANGE_LIGHT);
|
||||
draw_list->AddCircleFilled(center, radius, color);
|
||||
}
|
||||
|
||||
} // namespace priv
|
||||
|
||||
void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit)
|
||||
{
|
||||
if (!m_gui_cfg.has_value()) initialize();
|
||||
check_selection();
|
||||
set_volume_by_selection();
|
||||
|
||||
// Do not render window for not selected text volume
|
||||
if (m_volume == nullptr || !m_volume->text_configuration.has_value()) {
|
||||
@ -655,6 +718,8 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit)
|
||||
const ImVec2 &min_window_size = get_minimal_window_size();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, min_window_size);
|
||||
|
||||
priv::draw_origin_ball(m_parent);
|
||||
|
||||
#ifdef SHOW_FINE_POSITION
|
||||
draw_fine_position(m_parent.get_selection(), m_parent.get_canvas_size(), min_window_size);
|
||||
#endif // SHOW_FINE_POSITION
|
||||
@ -665,13 +730,19 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit)
|
||||
draw_mouse_offset(m_dragging_mouse_offset);
|
||||
#endif // SHOW_OFFSET_DURING_DRAGGING
|
||||
|
||||
ImGuiWindowFlags flag = ImGuiWindowFlags_NoCollapse;
|
||||
if (m_allow_float_window){
|
||||
// check if is set window offset
|
||||
if (m_set_window_offset.has_value()) {
|
||||
ImGui::SetNextWindowPos(*m_set_window_offset, ImGuiCond_Always);
|
||||
m_set_window_offset.reset();
|
||||
}
|
||||
} else {
|
||||
flag |= ImGuiWindowFlags_NoMove;
|
||||
y = std::min(y, bottom_limit - min_window_size.y);
|
||||
ImGui::SetNextWindowPos(ImVec2(x, y), ImGuiCond_Always);
|
||||
}
|
||||
|
||||
ImGuiWindowFlags flag = ImGuiWindowFlags_NoCollapse;
|
||||
if (ImGui::Begin(on_get_name().c_str(), nullptr, flag)) {
|
||||
// Need to pop var before draw window
|
||||
ImGui::PopStyleVar(); // WindowMinSize
|
||||
@ -682,6 +753,29 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit)
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
namespace priv {
|
||||
/// <summary>
|
||||
/// Move window for edit emboss text near to embossed object
|
||||
/// NOTE: embossed object must be selected
|
||||
/// </summary>
|
||||
ImVec2 calc_fine_position(const Selection &selection, const ImVec2 &windows_size, const Size &canvas_size)
|
||||
{
|
||||
const Selection::IndicesList indices = selection.get_volume_idxs();
|
||||
// no selected volume
|
||||
if (indices.empty()) return {};
|
||||
const GLVolume *volume = selection.get_volume(*indices.begin());
|
||||
// bad volume selected (e.g. deleted one)
|
||||
if (volume == nullptr) return {};
|
||||
|
||||
const Camera &camera = wxGetApp().plater()->get_camera();
|
||||
Slic3r::Polygon hull = CameraUtils::create_hull2d(camera, *volume);
|
||||
|
||||
ImVec2 c_size(canvas_size.get_width(), canvas_size.get_height());
|
||||
ImVec2 offset = ImGuiWrapper::suggest_location(windows_size, hull, c_size);
|
||||
return offset;
|
||||
}
|
||||
} // namespace priv
|
||||
|
||||
void GLGizmoEmboss::on_set_state()
|
||||
{
|
||||
// enable / disable bed from picking
|
||||
@ -714,10 +808,19 @@ void GLGizmoEmboss::on_set_state()
|
||||
wxFontEnumerator::InvalidateCache();
|
||||
|
||||
// Try(when exist) set text configuration by volume
|
||||
load_configuration(get_selected_volume());
|
||||
set_volume(priv::get_selected_volume(m_parent.get_selection()));
|
||||
|
||||
// when open window by "T" and no valid volume is selected, so Create new one
|
||||
if (m_volume == nullptr) {
|
||||
// reopen gizmo when new object is created
|
||||
GLGizmoBase::m_state = GLGizmoBase::Off;
|
||||
// start creating new object
|
||||
create_volume(ModelVolumeType::MODEL_PART);
|
||||
}
|
||||
|
||||
// change position of just opened emboss window
|
||||
set_fine_position();
|
||||
if (m_allow_float_window)
|
||||
m_set_window_offset = priv::calc_fine_position(m_parent.get_selection(), get_minimal_window_size(), m_parent.get_canvas_size());
|
||||
|
||||
// when open by hyperlink it needs to show up
|
||||
// or after key 'T' windows doesn't appear
|
||||
@ -872,9 +975,9 @@ EmbossStyles GLGizmoEmboss::create_default_styles()
|
||||
void GLGizmoEmboss::set_default_text(){ m_text = _u8L("Embossed text"); }
|
||||
|
||||
#include "imgui/imgui_internal.h" // to unfocus input --> ClearActiveID
|
||||
void GLGizmoEmboss::check_selection()
|
||||
void GLGizmoEmboss::set_volume_by_selection()
|
||||
{
|
||||
ModelVolume *vol = get_selected_volume();
|
||||
ModelVolume *vol = priv::get_selected_volume(m_parent.get_selection());
|
||||
// is same volume selected?
|
||||
if (vol != nullptr && m_volume == vol) return;
|
||||
|
||||
@ -885,27 +988,88 @@ void GLGizmoEmboss::check_selection()
|
||||
if (m_volume != nullptr) ImGui::ClearActiveID();
|
||||
|
||||
// is select embossed volume?
|
||||
if (load_configuration(vol))
|
||||
// successfull load volume for editing
|
||||
return;
|
||||
|
||||
// behave like adding new text
|
||||
if (!set_volume(vol)) {
|
||||
// Can't load so behave like adding new text
|
||||
m_volume = nullptr;
|
||||
set_default_text();
|
||||
}
|
||||
}
|
||||
|
||||
bool GLGizmoEmboss::set_volume(ModelVolume *volume)
|
||||
{
|
||||
if (volume == nullptr) return false;
|
||||
const std::optional<TextConfiguration> tc_opt = volume->text_configuration;
|
||||
if (!tc_opt.has_value()) return false;
|
||||
const TextConfiguration &tc = *tc_opt;
|
||||
const EmbossStyle &style = tc.style;
|
||||
|
||||
auto has_same_name = [&style](const StyleManager::Item &style_item) -> bool {
|
||||
const EmbossStyle &es = style_item.style;
|
||||
return es.name == style.name;
|
||||
};
|
||||
|
||||
wxFont wx_font;
|
||||
bool is_path_changed = false;
|
||||
if (style.type == WxFontUtils::get_actual_type())
|
||||
wx_font = WxFontUtils::load_wxFont(style.path);
|
||||
if (!wx_font.IsOk()) {
|
||||
create_notification_not_valid_font(tc);
|
||||
// Try create similar wx font
|
||||
wx_font = WxFontUtils::create_wxFont(style);
|
||||
is_path_changed = wx_font.IsOk();
|
||||
}
|
||||
|
||||
const auto& styles = m_style_manager.get_styles();
|
||||
auto it = std::find_if(styles.begin(), styles.end(), has_same_name);
|
||||
if (it == styles.end()) {
|
||||
// style was not found
|
||||
if (wx_font.IsOk())
|
||||
m_style_manager.load_style(style, wx_font);
|
||||
} else {
|
||||
size_t style_index = it - styles.begin();
|
||||
if (!m_style_manager.load_style(style_index)) {
|
||||
// can`t load stored style
|
||||
m_style_manager.erase(style_index);
|
||||
if (wx_font.IsOk())
|
||||
m_style_manager.load_style(style, wx_font);
|
||||
|
||||
} else {
|
||||
// stored style is loaded, now set modification of style
|
||||
m_style_manager.get_style() = style;
|
||||
m_style_manager.set_wx_font(wx_font);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_path_changed) {
|
||||
std::string path = WxFontUtils::store_wxFont(wx_font);
|
||||
m_style_manager.get_style().path = path;
|
||||
}
|
||||
|
||||
m_text = tc.text;
|
||||
m_volume = volume;
|
||||
|
||||
// store volume state before edit
|
||||
m_unmodified_volume = {*volume->get_mesh_shared_ptr(), // copy
|
||||
tc, volume->get_matrix(), volume->name};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ModelVolume *priv::get_model_volume(const GLVolume *gl_volume, const ModelObject *object)
|
||||
{
|
||||
int volume_id = gl_volume->volume_idx();
|
||||
if (volume_id < 0 || static_cast<size_t>(volume_id) >= object->volumes.size()) return nullptr;
|
||||
return object->volumes[volume_id];
|
||||
}
|
||||
|
||||
ModelVolume *priv::get_model_volume(const GLVolume *gl_volume, const ModelObjectPtrs &objects)
|
||||
{
|
||||
const GLVolume::CompositeID &id = gl_volume->composite_id;
|
||||
|
||||
if (id.object_id < 0 || static_cast<size_t>(id.object_id) >= objects.size()) return nullptr;
|
||||
ModelObject *object = objects[id.object_id];
|
||||
|
||||
if (id.volume_id < 0 || static_cast<size_t>(id.volume_id) >= object->volumes.size()) return nullptr;
|
||||
return object->volumes[id.volume_id];
|
||||
int object_id = gl_volume->object_idx();
|
||||
if (object_id < 0 || static_cast<size_t>(object_id) >= objects.size()) return nullptr;
|
||||
return get_model_volume(gl_volume, objects[object_id]);
|
||||
}
|
||||
|
||||
ModelVolume *priv::get_selected_volume(const Selection &selection, const ModelObjectPtrs &objects)
|
||||
ModelVolume *priv::get_selected_volume(const Selection &selection)
|
||||
{
|
||||
int object_idx = selection.get_object_idx();
|
||||
// is more object selected?
|
||||
@ -916,15 +1080,10 @@ ModelVolume *priv::get_selected_volume(const Selection &selection, const ModelOb
|
||||
if (volume_idxs.size() != 1) return nullptr;
|
||||
unsigned int vol_id_gl = *volume_idxs.begin();
|
||||
const GLVolume *vol_gl = selection.get_volume(vol_id_gl);
|
||||
const ModelObjectPtrs &objects = selection.get_model()->objects;
|
||||
return get_model_volume(vol_gl, objects);
|
||||
}
|
||||
|
||||
ModelVolume *GLGizmoEmboss::get_selected_volume()
|
||||
{
|
||||
return priv::get_selected_volume(m_parent.get_selection(),
|
||||
wxGetApp().plater()->model().objects);
|
||||
}
|
||||
|
||||
// Run Job on main thread (blocking) - ONLY DEBUG
|
||||
static inline void execute_job(std::shared_ptr<Job> j)
|
||||
{
|
||||
@ -954,7 +1113,7 @@ bool GLGizmoEmboss::process()
|
||||
if (m_text.empty()) return false;
|
||||
|
||||
// exist loaded font file?
|
||||
if (!m_style_manager.is_activ_font()) return false;
|
||||
if (!m_style_manager.is_active_font()) return false;
|
||||
|
||||
// Cancel previous Job, when it is in process
|
||||
// Can't use cancel, because I want cancel only previous EmbossUpdateJob no other jobs
|
||||
@ -971,11 +1130,8 @@ bool GLGizmoEmboss::process()
|
||||
// check cutting from source mesh
|
||||
bool &use_surface = data.text_configuration.style.prop.use_surface;
|
||||
bool is_object = m_volume->get_object()->volumes.size() == 1;
|
||||
if (use_surface && is_object) {
|
||||
priv::message_disable_cut_surface();
|
||||
if (use_surface && is_object)
|
||||
use_surface = false;
|
||||
}
|
||||
|
||||
|
||||
if (use_surface) {
|
||||
// Model to cut surface from.
|
||||
@ -1053,6 +1209,62 @@ void GLGizmoEmboss::discard_and_close() {
|
||||
// * Volume containing 3mf fix transformation - needs work around
|
||||
}
|
||||
|
||||
namespace priv {
|
||||
|
||||
/// <summary>
|
||||
/// Apply camera direction for emboss direction
|
||||
/// </summary>
|
||||
/// <param name="camera">Define view vector</param>
|
||||
/// <param name="canvas">Containe Selected Model to modify</param>
|
||||
/// <returns>True when apply change otherwise false</returns>
|
||||
static bool apply_camera_dir(const Camera &camera, GLCanvas3D &canvas);
|
||||
}
|
||||
|
||||
bool priv::apply_camera_dir(const Camera &camera, GLCanvas3D &canvas) {
|
||||
const Vec3d &cam_dir = camera.get_dir_forward();
|
||||
|
||||
Selection &sel = canvas.get_selection();
|
||||
if (sel.is_empty()) return false;
|
||||
|
||||
// camera direction transformed into volume coordinate system
|
||||
Transform3d to_world = priv::world_matrix(sel);
|
||||
Vec3d cam_dir_tr = to_world.inverse().linear() * cam_dir;
|
||||
cam_dir_tr.normalize();
|
||||
|
||||
Vec3d emboss_dir(0., 0., -1.);
|
||||
|
||||
// check wether cam_dir is already used
|
||||
if (is_approx(cam_dir_tr, emboss_dir)) return false;
|
||||
|
||||
assert(sel.get_volume_idxs().size() == 1);
|
||||
GLVolume *vol = sel.get_volume(*sel.get_volume_idxs().begin());
|
||||
|
||||
Transform3d vol_rot;
|
||||
Transform3d vol_tr = vol->get_volume_transformation().get_matrix();
|
||||
// check whether cam_dir is opposit to emboss dir
|
||||
if (is_approx(cam_dir_tr, -emboss_dir)) {
|
||||
// rotate 180 DEG by y
|
||||
vol_rot = Eigen::AngleAxis(M_PI_2, Vec3d(0., 1., 0.));
|
||||
} else {
|
||||
// calc params for rotation
|
||||
Vec3d axe = emboss_dir.cross(cam_dir_tr);
|
||||
axe.normalize();
|
||||
double angle = std::acos(emboss_dir.dot(cam_dir_tr));
|
||||
vol_rot = Eigen::AngleAxis(angle, axe);
|
||||
}
|
||||
|
||||
Vec3d offset = vol_tr * Vec3d::Zero();
|
||||
Vec3d offset_inv = vol_rot.inverse() * offset;
|
||||
Transform3d res = vol_tr *
|
||||
Eigen::Translation<double, 3>(-offset) *
|
||||
vol_rot *
|
||||
Eigen::Translation<double, 3>(offset_inv);
|
||||
//Transform3d res = vol_tr * vol_rot;
|
||||
vol->set_volume_transformation(res);
|
||||
priv::get_model_volume(vol, sel.get_model()->objects)->set_transformation(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLGizmoEmboss::draw_window()
|
||||
{
|
||||
#ifdef ALLOW_DEBUG_MODE
|
||||
@ -1060,8 +1272,8 @@ void GLGizmoEmboss::draw_window()
|
||||
if (ImGui::Button("add svg")) choose_svg_file();
|
||||
#endif // ALLOW_DEBUG_MODE
|
||||
|
||||
bool is_activ_font = m_style_manager.is_activ_font();
|
||||
if (!is_activ_font)
|
||||
bool is_active_font = m_style_manager.is_active_font();
|
||||
if (!is_active_font)
|
||||
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Warning: No font is selected. Select correct one."));
|
||||
|
||||
// Disable all except selection of font, when open text from 3mf with unknown font
|
||||
@ -1073,7 +1285,7 @@ void GLGizmoEmboss::draw_window()
|
||||
draw_text_input();
|
||||
draw_model_type();
|
||||
draw_style_list();
|
||||
m_imgui->disabled_begin(!is_activ_font);
|
||||
m_imgui->disabled_begin(!is_active_font);
|
||||
ImGui::TreePush();
|
||||
draw_style_edit();
|
||||
ImGui::TreePop();
|
||||
@ -1091,7 +1303,7 @@ void GLGizmoEmboss::draw_window()
|
||||
ImGui::TreePop();
|
||||
} else if (m_is_advanced_edit_style)
|
||||
set_minimal_window_size(false);
|
||||
m_imgui->disabled_end(); // !is_activ_font
|
||||
m_imgui->disabled_end(); // !is_active_font
|
||||
|
||||
#ifdef SHOW_WX_FONT_DESCRIPTOR
|
||||
if (is_selected_style)
|
||||
@ -1143,6 +1355,29 @@ void GLGizmoEmboss::draw_window()
|
||||
const auto &atlas = m_style_manager.get_atlas();
|
||||
ImGui::Image(atlas.TexID, ImVec2(atlas.TexWidth, atlas.TexHeight));
|
||||
#endif // SHOW_IMGUI_ATLAS
|
||||
|
||||
#ifdef ALLOW_FLOAT_WINDOW
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Checkbox("##allow_float_window", &m_allow_float_window)) {
|
||||
if (m_allow_float_window)
|
||||
m_set_window_offset = priv::calc_fine_position(m_parent.get_selection(), get_minimal_window_size(), m_parent.get_canvas_size());
|
||||
} else if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s", ((m_allow_float_window) ?
|
||||
_u8L("Fix settings possition"):
|
||||
_u8L("Allow floating window near text")).c_str());
|
||||
}
|
||||
#endif // ALLOW_FLOAT_WINDOW
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("use")) {
|
||||
assert(priv::get_selected_volume(m_parent.get_selection()) == m_volume);
|
||||
const Camera& cam = wxGetApp().plater()->get_camera();
|
||||
bool use_surface = m_style_manager.get_style().prop.use_surface;
|
||||
if (priv::apply_camera_dir(cam, m_parent) && use_surface)
|
||||
process();
|
||||
} else if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s", _u8L("Use camera direction for text orientation").c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void GLGizmoEmboss::draw_text_input()
|
||||
@ -1570,7 +1805,7 @@ void GLGizmoEmboss::draw_font_list()
|
||||
{
|
||||
// Set partial
|
||||
wxString actual_face_name;
|
||||
if (m_style_manager.is_activ_font()) {
|
||||
if (m_style_manager.is_active_font()) {
|
||||
const std::optional<wxFont> &wx_font_opt = m_style_manager.get_wx_font();
|
||||
if (wx_font_opt.has_value())
|
||||
actual_face_name = wx_font_opt->GetFaceName();
|
||||
@ -1816,6 +2051,7 @@ void GLGizmoEmboss::draw_style_rename_button()
|
||||
else ImGui::SetTooltip("%s", _u8L("Can't rename temporary style.").c_str());
|
||||
}
|
||||
if (ImGui::BeginPopupModal(popup_id, 0, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
m_imgui->disable_background_fadeout_animation();
|
||||
draw_style_rename_popup();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
@ -1910,6 +2146,7 @@ void GLGizmoEmboss::draw_style_add_button()
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopupModal(popup_id, 0, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
m_imgui->disable_background_fadeout_animation();
|
||||
draw_style_save_as_popup();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
@ -1926,9 +2163,9 @@ void GLGizmoEmboss::draw_delete_style_button() {
|
||||
if (draw_button(IconType::erase, !can_delete)) {
|
||||
while (true) {
|
||||
// NOTE: can't use previous loaded activ index -> erase could change index
|
||||
size_t activ_index = m_style_manager.get_style_index();
|
||||
next_style_index = (activ_index > 0) ? activ_index - 1 :
|
||||
activ_index + 1;
|
||||
size_t active_index = m_style_manager.get_style_index();
|
||||
next_style_index = (active_index > 0) ? active_index - 1 :
|
||||
active_index + 1;
|
||||
if (next_style_index >= m_style_manager.get_styles().size()) {
|
||||
// can't remove last font style
|
||||
// TODO: inform user
|
||||
@ -1942,7 +2179,7 @@ void GLGizmoEmboss::draw_delete_style_button() {
|
||||
}
|
||||
|
||||
// load back
|
||||
m_style_manager.load_style(activ_index);
|
||||
m_style_manager.load_style(active_index);
|
||||
ImGui::OpenPopup(popup_id);
|
||||
break;
|
||||
}
|
||||
@ -1958,13 +2195,14 @@ void GLGizmoEmboss::draw_delete_style_button() {
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopupModal(popup_id)) {
|
||||
m_imgui->disable_background_fadeout_animation();
|
||||
const std::string &style_name = m_style_manager.get_style().name;
|
||||
std::string text_in_popup = GUI::format(_L("Are you sure,\nthat you want permanently and unrecoverable \nremove style \"%1%\"?"), style_name);
|
||||
ImGui::Text("%s", text_in_popup.c_str());
|
||||
if (ImGui::Button(_u8L("Yes").c_str())) {
|
||||
size_t activ_index = m_style_manager.get_style_index();
|
||||
size_t active_index = m_style_manager.get_style_index();
|
||||
m_style_manager.load_style(next_style_index);
|
||||
m_style_manager.erase(activ_index);
|
||||
m_style_manager.erase(active_index);
|
||||
m_style_manager.store_styles_to_app_config(wxGetApp().app_config);
|
||||
ImGui::CloseCurrentPopup();
|
||||
process();
|
||||
@ -2000,7 +2238,7 @@ void GLGizmoEmboss::fix_transformation(const FontProp &from,
|
||||
}
|
||||
|
||||
void GLGizmoEmboss::draw_style_list() {
|
||||
if (!m_style_manager.is_activ_font()) return;
|
||||
if (!m_style_manager.is_active_font()) return;
|
||||
|
||||
const EmbossStyle *stored_style = nullptr;
|
||||
bool is_stored = m_style_manager.exist_stored_style();
|
||||
@ -2286,6 +2524,43 @@ bool GLGizmoEmboss::rev_input(const std::string &name,
|
||||
return revertible(name, value, default_value, undo_tooltip, undo_offset, draw_offseted_input);
|
||||
}
|
||||
|
||||
bool GLGizmoEmboss::rev_input_mm(const std::string &name,
|
||||
float &value,
|
||||
const float *default_value_ptr,
|
||||
const std::string &undo_tooltip,
|
||||
float step,
|
||||
float step_fast,
|
||||
const char *format,
|
||||
bool use_inch,
|
||||
std::optional<float> scale)
|
||||
{
|
||||
// _variable which temporary keep value
|
||||
float value_ = value;
|
||||
float default_value_;
|
||||
if (use_inch) {
|
||||
// calc value in inch
|
||||
value_ *= ObjectManipulation::mm_to_in;
|
||||
if (default_value_ptr) {
|
||||
default_value_ = ObjectManipulation::mm_to_in * (*default_value_ptr);
|
||||
default_value_ptr = &default_value_;
|
||||
}
|
||||
}
|
||||
if (scale.has_value())
|
||||
value_ *= *scale;
|
||||
bool use_correction = use_inch || scale.has_value();
|
||||
if (rev_input(name, use_correction ? value_ : value, default_value_ptr, undo_tooltip, step, step_fast, format)) {
|
||||
if (use_correction) {
|
||||
value = value_;
|
||||
if (use_inch)
|
||||
value *= ObjectManipulation::in_to_mm;
|
||||
if (scale.has_value())
|
||||
value /= *scale;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLGizmoEmboss::rev_checkbox(const std::string &name,
|
||||
bool &value,
|
||||
const bool *default_value,
|
||||
@ -2301,12 +2576,48 @@ bool GLGizmoEmboss::rev_checkbox(const std::string &name,
|
||||
undo_offset, draw_offseted_input);
|
||||
}
|
||||
|
||||
bool is_font_changed(
|
||||
const wxFont &wx_font, const wxFont &wx_font_stored,
|
||||
const FontProp &prop, const FontProp &prop_stored)
|
||||
{
|
||||
// Exist change in face name?
|
||||
if(wx_font_stored.GetFaceName() != wx_font.GetFaceName()) return true;
|
||||
|
||||
const std::optional<float> &skew = prop.skew;
|
||||
bool is_italic = skew.has_value() || WxFontUtils::is_italic(wx_font);
|
||||
const std::optional<float> &skew_stored = prop_stored.skew;
|
||||
bool is_stored_italic = skew_stored.has_value() || WxFontUtils::is_italic(wx_font_stored);
|
||||
// is italic changed
|
||||
if (is_italic != is_stored_italic)
|
||||
return true;
|
||||
|
||||
const std::optional<float> &boldness = prop.boldness;
|
||||
bool is_bold = boldness.has_value() || WxFontUtils::is_bold(wx_font);
|
||||
const std::optional<float> &boldness_stored = prop_stored.boldness;
|
||||
bool is_stored_bold = boldness_stored.has_value() || WxFontUtils::is_bold(wx_font_stored);
|
||||
// is bold changed
|
||||
return is_bold != is_stored_bold;
|
||||
}
|
||||
|
||||
bool is_font_changed(const StyleManager &mng) {
|
||||
const std::optional<wxFont> &wx_font_opt = mng.get_wx_font();
|
||||
if (!wx_font_opt.has_value())
|
||||
return false;
|
||||
if (!mng.exist_stored_style())
|
||||
return false;
|
||||
const EmbossStyle *stored_style = mng.get_stored_style();
|
||||
if (stored_style == nullptr)
|
||||
return false;
|
||||
|
||||
const std::optional<wxFont> &wx_font_stored_opt = mng.get_stored_wx_font();
|
||||
if (!wx_font_stored_opt.has_value())
|
||||
return false;
|
||||
|
||||
return is_font_changed(*wx_font_opt, *wx_font_stored_opt, mng.get_style().prop, stored_style->prop);
|
||||
}
|
||||
|
||||
void GLGizmoEmboss::draw_style_edit() {
|
||||
const GuiCfg::Translations &tr = m_gui_cfg->translations;
|
||||
|
||||
const std::optional<wxFont> &wx_font_opt = m_style_manager.get_wx_font();
|
||||
EmbossStyle &style = m_style_manager.get_style();
|
||||
|
||||
assert(wx_font_opt.has_value());
|
||||
if (!wx_font_opt.has_value()) {
|
||||
ImGui::TextColored(ImGuiWrapper::COL_ORANGE_DARK, "%s", _u8L("WxFont is not loaded properly.").c_str());
|
||||
@ -2314,33 +2625,9 @@ void GLGizmoEmboss::draw_style_edit() {
|
||||
}
|
||||
|
||||
bool exist_stored_style = m_style_manager.exist_stored_style();
|
||||
bool is_font_changed = false;
|
||||
if (exist_stored_style && wx_font_opt.has_value()) {
|
||||
const wxFont &wx_font = *wx_font_opt;
|
||||
const EmbossStyle *stored_style = m_style_manager.get_stored_style();
|
||||
assert(stored_style != nullptr);
|
||||
const std::optional<wxFont> &stored_wx = m_style_manager.get_stored_wx_font();
|
||||
assert(stored_wx.has_value());
|
||||
bool is_font_face_changed = stored_wx->GetFaceName() != wx_font.GetFaceName();
|
||||
|
||||
const std::optional<float> &skew = m_style_manager.get_font_prop().skew;
|
||||
bool is_italic = skew.has_value() || WxFontUtils::is_italic(wx_font);
|
||||
const std::optional<float> &skew_stored = stored_style->prop.skew;
|
||||
bool is_stored_italic = skew_stored.has_value() || WxFontUtils::is_italic(*stored_wx);
|
||||
bool is_italic_changed = is_italic != is_stored_italic;
|
||||
|
||||
const std::optional<float> &boldness = m_style_manager.get_font_prop().boldness;
|
||||
bool is_bold = boldness.has_value() || WxFontUtils::is_bold(wx_font);
|
||||
const std::optional<float> &boldness_stored = stored_style->prop.boldness;
|
||||
bool is_stored_bold = boldness_stored.has_value() || WxFontUtils::is_bold(*stored_wx);
|
||||
bool is_bold_changed = is_bold != is_stored_bold;
|
||||
|
||||
bool is_font_style_changed = is_italic_changed || is_bold_changed;
|
||||
|
||||
is_font_changed = is_font_face_changed || is_font_style_changed;
|
||||
}
|
||||
|
||||
if (is_font_changed || !exist_stored_style)
|
||||
bool exist_change_in_font = is_font_changed(m_style_manager);
|
||||
const GuiCfg::Translations &tr = m_gui_cfg->translations;
|
||||
if (exist_change_in_font || !exist_stored_style)
|
||||
ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr.font);
|
||||
else
|
||||
ImGuiWrapper::text(tr.font);
|
||||
@ -2354,7 +2641,8 @@ void GLGizmoEmboss::draw_style_edit() {
|
||||
ImGui::SameLine();
|
||||
if (draw_bold_button()) exist_change = true;
|
||||
|
||||
if (is_font_changed) {
|
||||
EmbossStyle &style = m_style_manager.get_style();
|
||||
if (exist_change_in_font) {
|
||||
ImGui::SameLine(ImGui::GetStyle().FramePadding.x);
|
||||
if (draw_button(IconType::undo)) {
|
||||
const EmbossStyle *stored_style = m_style_manager.get_stored_style();
|
||||
@ -2376,41 +2664,16 @@ void GLGizmoEmboss::draw_style_edit() {
|
||||
}
|
||||
|
||||
bool use_inch = wxGetApp().app_config->get("use_inches") == "1";
|
||||
const std::string revert_text_size = _u8L("Revert text size.");
|
||||
FontProp &font_prop = style.prop;
|
||||
const float * def_size = exist_stored_style?
|
||||
&m_style_manager.get_stored_style()->prop.size_in_mm : nullptr;
|
||||
bool is_size_changed = false;
|
||||
if (use_inch) {
|
||||
float size_in_inch = ObjectManipulation::mm_to_in * font_prop.size_in_mm;
|
||||
float def_size_inch = exist_stored_style ? ObjectManipulation::mm_to_in * (*def_size) : 0.f;
|
||||
if (def_size != nullptr) def_size = &def_size_inch;
|
||||
if (rev_input(tr.size, size_in_inch, def_size, revert_text_size, 0.1f, 1.f, "%.2f in")) {
|
||||
font_prop.size_in_mm = ObjectManipulation::in_to_mm * size_in_inch;
|
||||
is_size_changed = true;
|
||||
}
|
||||
} else {
|
||||
if (rev_input(tr.size, font_prop.size_in_mm, def_size, revert_text_size, 0.1f, 1.f, "%.1f mm"))
|
||||
is_size_changed = true;
|
||||
}
|
||||
|
||||
if (is_size_changed) {
|
||||
// size can't be zero or negative
|
||||
Limits::apply(font_prop.size_in_mm, limits.size_in_mm);
|
||||
// IMPROVE: calc scale only when neccessary not each frame
|
||||
Transform3d to_world = priv::world_matrix(m_parent.get_selection());
|
||||
Vec3d up_world = to_world.linear() * Vec3d(0., 1., 0.);
|
||||
double norm_sq = up_world.squaredNorm();
|
||||
std::optional<float> height_scale;
|
||||
if (!is_approx(norm_sq, 1.))
|
||||
height_scale = sqrt(norm_sq);
|
||||
|
||||
// only different value need process
|
||||
if (!is_approx(font_prop.size_in_mm, m_volume->text_configuration->style.prop.size_in_mm)) {
|
||||
// store font size into path
|
||||
if (style.type == WxFontUtils::get_actual_type()) {
|
||||
if (wx_font_opt.has_value()) {
|
||||
wxFont wx_font = *wx_font_opt;
|
||||
wx_font.SetPointSize(static_cast<int>(font_prop.size_in_mm));
|
||||
m_style_manager.set_wx_font(wx_font);
|
||||
}
|
||||
}
|
||||
process();
|
||||
}
|
||||
}
|
||||
draw_height(height_scale, use_inch);
|
||||
|
||||
#ifdef SHOW_WX_WEIGHT_INPUT
|
||||
if (wx_font.has_value()) {
|
||||
@ -2438,28 +2701,56 @@ void GLGizmoEmboss::draw_style_edit() {
|
||||
}
|
||||
#endif // SHOW_WX_WEIGHT_INPUT
|
||||
|
||||
const std::string revert_emboss_depth = _u8L("Revert embossed depth.");
|
||||
const float *def_depth = exist_stored_style ?
|
||||
&m_style_manager.get_stored_style()->prop.emboss : nullptr;
|
||||
bool is_depth_changed = false;
|
||||
if (use_inch) {
|
||||
float depthj_in_inch = ObjectManipulation::mm_to_in * font_prop.emboss;
|
||||
float def_depth_inch = exist_stored_style ? ObjectManipulation::mm_to_in * (*def_depth) : 0.f;
|
||||
if (def_depth != nullptr) def_depth = &def_depth_inch;
|
||||
if (rev_input(tr.depth, depthj_in_inch, def_depth, revert_emboss_depth, 0.1f, 0.25, "%.3f in")) {
|
||||
font_prop.emboss = ObjectManipulation::in_to_mm * depthj_in_inch;
|
||||
is_depth_changed = true;
|
||||
}
|
||||
} else {
|
||||
if (rev_input(tr.depth, font_prop.emboss, def_depth, revert_emboss_depth, 0.1f, 0.25, "%.2f mm"))
|
||||
is_depth_changed = true;
|
||||
Vec3d depth_world = to_world.linear() * Vec3d(0., 0., 1.);
|
||||
double depth_sq = depth_world.squaredNorm();
|
||||
std::optional<float> depth_scale;
|
||||
if (!is_approx(depth_sq, 1.)) depth_scale = sqrt(depth_sq);
|
||||
draw_depth(depth_scale, use_inch);
|
||||
}
|
||||
|
||||
if (is_depth_changed) {
|
||||
Limits::apply(font_prop.emboss, limits.emboss);
|
||||
void GLGizmoEmboss::draw_height(std::optional<float> scale, bool use_inch)
|
||||
{
|
||||
float &value = m_style_manager.get_style().prop.size_in_mm;
|
||||
const EmbossStyle* stored_style = m_style_manager.get_stored_style();
|
||||
const float *stored = ((stored_style)? &stored_style->prop.size_in_mm : nullptr);
|
||||
const char *size_format = ((use_inch) ? "%.2f in" : "%.1f mm");
|
||||
const std::string revert_text_size = _u8L("Revert text size.");
|
||||
const std::string& name = m_gui_cfg->translations.size;
|
||||
if(rev_input_mm(name, value, stored, revert_text_size, 0.1f, 1.f, size_format, use_inch, scale)){
|
||||
// size can't be zero or negative
|
||||
Limits::apply(value, limits.size_in_mm);
|
||||
// only different value need process
|
||||
if (!is_approx(value, m_volume->text_configuration->style.prop.size_in_mm)) {
|
||||
// store font size into path
|
||||
EmbossStyle &style = m_style_manager.get_style();
|
||||
if (style.type == WxFontUtils::get_actual_type()) {
|
||||
const std::optional<wxFont> &wx_font_opt = m_style_manager.get_wx_font();
|
||||
if (wx_font_opt.has_value()) {
|
||||
wxFont wx_font = *wx_font_opt;
|
||||
wx_font.SetPointSize(static_cast<int>(value));
|
||||
m_style_manager.set_wx_font(wx_font);
|
||||
}
|
||||
}
|
||||
process();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLGizmoEmboss::draw_depth(std::optional<float> scale, bool use_inch)
|
||||
{
|
||||
float &value = m_style_manager.get_style().prop.emboss;
|
||||
const EmbossStyle* stored_style = m_style_manager.get_stored_style();
|
||||
const float *stored = ((stored_style)? &stored_style->prop.emboss : nullptr);
|
||||
const std::string revert_emboss_depth = _u8L("Revert embossed depth.");
|
||||
const char *size_format = ((use_inch) ? "%.3f in" : "%.2f mm");
|
||||
const std::string name = m_gui_cfg->translations.depth;
|
||||
if (rev_input_mm(name, value, stored, revert_emboss_depth, 0.1f, 1.f, size_format, use_inch, scale)) {
|
||||
// size can't be zero or negative
|
||||
Limits::apply(value, limits.emboss);
|
||||
process();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool GLGizmoEmboss::rev_slider(const std::string &name,
|
||||
std::optional<int>& value,
|
||||
@ -2536,7 +2827,7 @@ void GLGizmoEmboss::do_translate(const Vec3d &relative_move)
|
||||
selection.setup_cache();
|
||||
selection.translate(relative_move, TransformationType::Local);
|
||||
|
||||
std::string snapshot_name; // empty meand no store undo / redo
|
||||
std::string snapshot_name; // empty mean no store undo / redo
|
||||
// NOTE: it use L instead of _L macro because prefix _ is appended inside
|
||||
// function do_move
|
||||
// snapshot_name = L("Set surface distance");
|
||||
@ -2560,34 +2851,6 @@ void GLGizmoEmboss::do_rotate(float relative_z_angle)
|
||||
m_parent.do_rotate(snapshot_name);
|
||||
}
|
||||
|
||||
void GLGizmoEmboss::set_fine_position()
|
||||
{
|
||||
const Selection &selection = m_parent.get_selection();
|
||||
const Selection::IndicesList indices = selection.get_volume_idxs();
|
||||
// no selected volume
|
||||
if (indices.empty()) return;
|
||||
const GLVolume *volume = selection.get_volume(*indices.begin());
|
||||
// bad volume selected (e.g. deleted one)
|
||||
if (volume == nullptr) return;
|
||||
|
||||
const Camera &camera = wxGetApp().plater()->get_camera();
|
||||
Polygon hull = CameraUtils::create_hull2d(camera, *volume);
|
||||
|
||||
const ImVec2 &windows_size = get_minimal_window_size();
|
||||
Size c_size = m_parent.get_canvas_size();
|
||||
ImVec2 canvas_size(c_size.get_width(), c_size.get_height());
|
||||
ImVec2 offset = ImGuiWrapper::suggest_location(windows_size, hull, canvas_size);
|
||||
m_set_window_offset = offset;
|
||||
return;
|
||||
|
||||
Polygon rect({Point(offset.x, offset.y),
|
||||
Point(offset.x + windows_size.x, offset.y),
|
||||
Point(offset.x + windows_size.x, offset.y + windows_size.y),
|
||||
Point(offset.x, offset.y + windows_size.y)});
|
||||
ImGuiWrapper::draw(hull);
|
||||
ImGuiWrapper::draw(rect);
|
||||
}
|
||||
|
||||
void GLGizmoEmboss::draw_advanced()
|
||||
{
|
||||
const auto &ff = m_style_manager.get_font_file_with_cache();
|
||||
@ -2957,66 +3220,6 @@ bool GLGizmoEmboss::choose_svg_file()
|
||||
//return add_volume(name, its);
|
||||
}
|
||||
|
||||
bool GLGizmoEmboss::load_configuration(ModelVolume *volume)
|
||||
{
|
||||
if (volume == nullptr) return false;
|
||||
const std::optional<TextConfiguration> tc_opt = volume->text_configuration;
|
||||
if (!tc_opt.has_value()) return false;
|
||||
const TextConfiguration &tc = *tc_opt;
|
||||
const EmbossStyle &style = tc.style;
|
||||
|
||||
auto has_same_name = [&style](const StyleManager::Item &style_item) -> bool {
|
||||
const EmbossStyle &es = style_item.style;
|
||||
return es.name == style.name;
|
||||
};
|
||||
|
||||
wxFont wx_font;
|
||||
bool is_path_changed = false;
|
||||
if (style.type == WxFontUtils::get_actual_type())
|
||||
wx_font = WxFontUtils::load_wxFont(style.path);
|
||||
if (!wx_font.IsOk()) {
|
||||
create_notification_not_valid_font(tc);
|
||||
// Try create similar wx font
|
||||
wx_font = WxFontUtils::create_wxFont(style);
|
||||
is_path_changed = wx_font.IsOk();
|
||||
}
|
||||
|
||||
const auto& styles = m_style_manager.get_styles();
|
||||
auto it = std::find_if(styles.begin(), styles.end(), has_same_name);
|
||||
if (it == styles.end()) {
|
||||
// style was not found
|
||||
if (wx_font.IsOk())
|
||||
m_style_manager.load_style(style, wx_font);
|
||||
} else {
|
||||
size_t style_index = it - styles.begin();
|
||||
if (!m_style_manager.load_style(style_index)) {
|
||||
// can`t load stored style
|
||||
m_style_manager.erase(style_index);
|
||||
if (wx_font.IsOk())
|
||||
m_style_manager.load_style(style, wx_font);
|
||||
|
||||
} else {
|
||||
// stored style is loaded, now set modification of style
|
||||
m_style_manager.get_style() = style;
|
||||
m_style_manager.set_wx_font(wx_font);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_path_changed) {
|
||||
std::string path = WxFontUtils::store_wxFont(wx_font);
|
||||
m_style_manager.get_style().path = path;
|
||||
}
|
||||
|
||||
m_text = tc.text;
|
||||
m_volume = volume;
|
||||
|
||||
// store volume state before edit
|
||||
m_unmodified_volume = {*volume->get_mesh_shared_ptr(), // copy
|
||||
tc, volume->get_matrix(), volume->name};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLGizmoEmboss::create_notification_not_valid_font(
|
||||
const TextConfiguration &tc)
|
||||
{
|
||||
@ -3227,7 +3430,7 @@ DataBase priv::create_emboss_data_base(const std::string &text, StyleManager& st
|
||||
};
|
||||
|
||||
auto create_configuration = [&]() -> TextConfiguration {
|
||||
if (!style_manager.is_activ_font()) {
|
||||
if (!style_manager.is_active_font()) {
|
||||
std::string default_text_for_emboss = _u8L("Embossed text");
|
||||
EmbossStyle es = style_manager.get_style();
|
||||
TextConfiguration tc{es, default_text_for_emboss};
|
||||
@ -3247,29 +3450,6 @@ DataBase priv::create_emboss_data_base(const std::string &text, StyleManager& st
|
||||
return Slic3r::GUI::Emboss::DataBase{style_manager.get_font_file_with_cache(), create_configuration(), create_volume_name()};
|
||||
}
|
||||
|
||||
|
||||
Transform3d priv::create_transformation_on_bed(const Vec2d &screen_coor, const Camera &camera, const std::vector<Vec2d> &bed_shape, double z)
|
||||
{
|
||||
// Create new object
|
||||
// calculate X,Y offset position for lay on platter in place of
|
||||
// mouse click
|
||||
Vec2d bed_coor = CameraUtils::get_z0_position(camera, screen_coor);
|
||||
|
||||
// check point is on build plate:
|
||||
Points bed_shape_;
|
||||
bed_shape_.reserve(bed_shape.size());
|
||||
for (const Vec2d &p : bed_shape) bed_shape_.emplace_back(p.cast<int>());
|
||||
Slic3r::Polygon bed(bed_shape_);
|
||||
if (!bed.contains(bed_coor.cast<int>()))
|
||||
// mouse pose is out of build plate so create object in center of plate
|
||||
bed_coor = bed.centroid().cast<double>();
|
||||
|
||||
Vec3d offset(bed_coor.x(), bed_coor.y(), z);
|
||||
// offset -= m_result.center();
|
||||
Transform3d::TranslationType tt(offset.x(), offset.y(), offset.z());
|
||||
return Transform3d(tt);
|
||||
}
|
||||
|
||||
void priv::start_create_object_job(DataBase &emboss_data, const Vec2d &coor)
|
||||
{
|
||||
// start creation of new object
|
||||
@ -3282,10 +3462,8 @@ void priv::start_create_object_job(DataBase &emboss_data, const Vec2d &coor)
|
||||
if (prop.distance.has_value()) prop.distance.reset();
|
||||
|
||||
// can't create new object with using surface
|
||||
if (prop.use_surface) {
|
||||
priv::message_disable_cut_surface();
|
||||
if (prop.use_surface)
|
||||
prop.use_surface = false;
|
||||
}
|
||||
|
||||
// Transform3d volume_tr = priv::create_transformation_on_bed(mouse_pos, camera, bed_shape, prop.emboss / 2);
|
||||
DataCreateObject data{std::move(emboss_data), coor, camera, bed_shape};
|
||||
@ -3305,7 +3483,6 @@ void priv::start_create_volume_job(const ModelObject *object,
|
||||
// Model to cut surface from.
|
||||
SurfaceVolumeData::ModelSources sources = create_sources(object->volumes);
|
||||
if (sources.empty()) {
|
||||
priv::message_disable_cut_surface();
|
||||
use_surface = false;
|
||||
} else {
|
||||
bool is_outside = volume_type == ModelVolumeType::MODEL_PART;
|
||||
|
@ -87,8 +87,10 @@ private:
|
||||
// localized default text
|
||||
void set_default_text();
|
||||
|
||||
void check_selection();
|
||||
ModelVolume *get_selected_volume();
|
||||
void set_volume_by_selection();
|
||||
// load text configuration from volume into gizmo
|
||||
bool set_volume(ModelVolume *volume);
|
||||
|
||||
// create volume from text - main functionality
|
||||
bool process();
|
||||
void close();
|
||||
@ -109,6 +111,9 @@ private:
|
||||
void draw_font_preview(FaceName &face, bool is_visible);
|
||||
void draw_font_list();
|
||||
void draw_style_edit();
|
||||
void draw_height(std::optional<float> scale, bool use_inch);
|
||||
void draw_depth(std::optional<float> scale, bool use_inch);
|
||||
|
||||
bool draw_italic_button();
|
||||
bool draw_bold_button();
|
||||
void draw_advanced();
|
||||
@ -119,11 +124,9 @@ private:
|
||||
void do_translate(const Vec3d& relative_move);
|
||||
void do_rotate(float relative_z_angle);
|
||||
|
||||
/// <summary>
|
||||
/// Move window for edit emboss text near to embossed object
|
||||
/// NOTE: embossed object must be selected
|
||||
/// </summary>
|
||||
void set_fine_position();
|
||||
bool rev_input_mm(const std::string &name, float &value, const float *default_value,
|
||||
const std::string &undo_tooltip, float step, float step_fast, const char *format,
|
||||
bool use_inch = false, std::optional<float> scale = {});
|
||||
|
||||
/// <summary>
|
||||
/// Reversible input float with option to restor default value
|
||||
@ -154,8 +157,6 @@ private:
|
||||
bool choose_true_type_file();
|
||||
bool choose_svg_file();
|
||||
|
||||
bool load_configuration(ModelVolume *volume);
|
||||
|
||||
// When open text loaded from .3mf it could be written with unknown font
|
||||
bool m_is_unknown_font;
|
||||
void create_notification_not_valid_font(const TextConfiguration& tc);
|
||||
@ -216,9 +217,12 @@ private:
|
||||
GuiCfg() = default;
|
||||
};
|
||||
std::optional<const GuiCfg> m_gui_cfg;
|
||||
bool m_is_advanced_edit_style = false;
|
||||
|
||||
// when true window will appear near to text
|
||||
bool m_allow_float_window = false;
|
||||
// setted only when wanted to use - not all the time
|
||||
std::optional<ImVec2> m_set_window_offset;
|
||||
bool m_is_advanced_edit_style = false;
|
||||
|
||||
Emboss::StyleManager m_style_manager;
|
||||
|
||||
|
@ -257,20 +257,39 @@ bool ImGuiWrapper::update_key_data(wxKeyEvent &evt)
|
||||
return false;
|
||||
}
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
auto to_string = [](wxEventType type) -> std::string {
|
||||
if (type == wxEVT_CHAR) return "Char";
|
||||
if (type == wxEVT_KEY_DOWN) return "KeyDown";
|
||||
if (type == wxEVT_KEY_UP) return "KeyUp";
|
||||
return "Other";
|
||||
};
|
||||
|
||||
if (evt.GetEventType() == wxEVT_CHAR) {
|
||||
wxEventType type = evt.GetEventType();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
BOOST_LOG_TRIVIAL(debug) << "ImGui - key event(" << to_string(type) << "):"
|
||||
//<< " Unicode(" << evt.GetUnicodeKey() << ")"
|
||||
<< " KeyCode(" << evt.GetKeyCode() << ")";
|
||||
|
||||
if (type == wxEVT_CHAR) {
|
||||
// Char event
|
||||
const auto key = evt.GetUnicodeKey();
|
||||
unsigned int key_u = static_cast<unsigned int>(key);
|
||||
|
||||
// Release BackSpace, Delete, ... when miss wxEVT_KEY_UP event
|
||||
// Already Fixed at begining of new frame
|
||||
//if (key_u >= 0 && key_u < IM_ARRAYSIZE(io.KeysDown) && io.KeysDown[key_u]) {
|
||||
// io.KeysDown[key_u] = false;
|
||||
//}
|
||||
|
||||
if (key != 0) {
|
||||
io.AddInputCharacter(key);
|
||||
}
|
||||
} else {
|
||||
} else if (type == wxEVT_KEY_DOWN || type == wxEVT_KEY_UP) {
|
||||
// Key up/down event
|
||||
int key = evt.GetKeyCode();
|
||||
wxCHECK_MSG(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown), false, "Received invalid key code");
|
||||
|
||||
io.KeysDown[key] = evt.GetEventType() == wxEVT_KEY_DOWN;
|
||||
io.KeysDown[key] = (type == wxEVT_KEY_DOWN);
|
||||
io.KeyShift = evt.ShiftDown();
|
||||
io.KeyCtrl = evt.ControlDown();
|
||||
io.KeyAlt = evt.AltDown();
|
||||
@ -282,6 +301,7 @@ bool ImGuiWrapper::update_key_data(wxKeyEvent &evt)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#include <array>
|
||||
void ImGuiWrapper::new_frame()
|
||||
{
|
||||
if (m_new_frame_open) {
|
||||
@ -292,6 +312,35 @@ void ImGuiWrapper::new_frame()
|
||||
init_font(true);
|
||||
}
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
// synchronize key states
|
||||
// when the application loses the focus it may happen that the key up event is not processed
|
||||
|
||||
// synchronize modifier keys
|
||||
constexpr std::array<std::pair<ImGuiKeyModFlags_, wxKeyCode>, 3> imgui_mod_keys{
|
||||
std::make_pair(ImGuiKeyModFlags_Ctrl, WXK_CONTROL),
|
||||
std::make_pair(ImGuiKeyModFlags_Shift, WXK_SHIFT),
|
||||
std::make_pair(ImGuiKeyModFlags_Alt, WXK_ALT)};
|
||||
for (const std::pair<ImGuiKeyModFlags_, wxKeyCode>& key : imgui_mod_keys) {
|
||||
if ((io.KeyMods & key.first) != 0 && !wxGetKeyState(key.second))
|
||||
io.KeyMods &= ~key.first;
|
||||
}
|
||||
|
||||
// Not sure if it is neccessary
|
||||
// values from 33 to 126 are reserved for the standard ASCII characters
|
||||
for (size_t i = 33; i <= 126; ++i) {
|
||||
wxKeyCode keycode = static_cast<wxKeyCode>(i);
|
||||
if (io.KeysDown[i] && keycode != WXK_NONE && !wxGetKeyState(keycode))
|
||||
io.KeysDown[i] = false;
|
||||
}
|
||||
|
||||
// special keys: delete, backspace, ...
|
||||
for (int key: io.KeyMap) {
|
||||
wxKeyCode keycode = static_cast<wxKeyCode>(key);
|
||||
if (io.KeysDown[key] && keycode != WXK_NONE && !wxGetKeyState(keycode))
|
||||
io.KeysDown[key] = false;
|
||||
}
|
||||
|
||||
ImGui::NewFrame();
|
||||
m_new_frame_open = true;
|
||||
}
|
||||
|
@ -483,7 +483,7 @@ TriangleMesh priv::create_mesh(DataBase &input, Fnc was_canceled, Job::Ctl& ctl)
|
||||
TriangleMesh priv::create_default_mesh()
|
||||
{
|
||||
// When cant load any font use default object loaded from file
|
||||
std::string path = Slic3r::resources_dir() + "/data/embossed_text.stl";
|
||||
std::string path = Slic3r::resources_dir() + "/data/embossed_text.obj";
|
||||
TriangleMesh triangle_mesh;
|
||||
if (!load_obj(path.c_str(), &triangle_mesh)) {
|
||||
// when can't load mesh use cube
|
||||
@ -527,10 +527,6 @@ void UpdateJob::update_volume(ModelVolume *volume,
|
||||
obj_list->update_name_in_list(object_idx, volume_idx);
|
||||
}
|
||||
|
||||
// update printable state on canvas
|
||||
if (volume->type() == ModelVolumeType::MODEL_PART)
|
||||
canvas->update_instance_printable_state_for_object((size_t) object_idx);
|
||||
|
||||
// Move object on bed
|
||||
if (GLGizmoEmboss::is_text_object(volume)) volume->get_object()->ensure_on_bed();
|
||||
|
||||
@ -679,7 +675,7 @@ OrthoProject3d priv::create_emboss_projection(
|
||||
bool is_outside, float emboss, Transform3d tr, SurfaceCut &cut)
|
||||
{
|
||||
// Offset of clossed side to model
|
||||
const float surface_offset = 1e-3f; // [in mm]
|
||||
const float surface_offset = 0.015f; // [in mm]
|
||||
float
|
||||
front_move = (is_outside) ? emboss : surface_offset,
|
||||
back_move = -((is_outside) ? surface_offset : emboss);
|
||||
|
@ -5,7 +5,6 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <libslic3r/Emboss.hpp>
|
||||
//#include <libslic3r/ModelVolumeType.hpp>
|
||||
#include "slic3r/Utils/RaycastManager.hpp"
|
||||
#include "slic3r/GUI/Camera.hpp"
|
||||
#include "Job.hpp"
|
||||
|
@ -154,6 +154,7 @@ void KBShortcutsDialog::fill_shortcuts()
|
||||
{ "L", L("Gizmo FDM paint-on supports") },
|
||||
{ "P", L("Gizmo FDM paint-on seam") },
|
||||
{ "N", L("Gizmo Multi Material painting") },
|
||||
{ "T", L("Gizmo Text emboss / engrave")},
|
||||
{ "Esc", L("Unselect gizmo or clear selection") },
|
||||
{ "K", L("Change camera type (perspective, orthographic)") },
|
||||
{ "B", L("Zoom to Bed") },
|
||||
|
@ -39,19 +39,19 @@ void StyleManager::init(AppConfig *app_config, const EmbossStyles &default_style
|
||||
m_style_items.push_back({style});
|
||||
}
|
||||
|
||||
std::optional<size_t> activ_index_opt = (app_config != nullptr) ?
|
||||
std::optional<size_t> active_index_opt = (app_config != nullptr) ?
|
||||
EmbossStylesSerializable::load_style_index(*app_config) :
|
||||
std::optional<size_t>{};
|
||||
|
||||
size_t activ_index = 0;
|
||||
if (activ_index_opt.has_value()) activ_index = *activ_index_opt;
|
||||
if (activ_index >= m_style_items.size()) activ_index = 0;
|
||||
size_t active_index = 0;
|
||||
if (active_index_opt.has_value()) active_index = *active_index_opt;
|
||||
if (active_index >= m_style_items.size()) active_index = 0;
|
||||
|
||||
// find valid font item
|
||||
if (!load_style(activ_index)) {
|
||||
m_style_items.erase(m_style_items.begin() + activ_index);
|
||||
activ_index = 0;
|
||||
while (m_style_items.empty() || !load_style(activ_index))
|
||||
if (!load_style(active_index)) {
|
||||
m_style_items.erase(m_style_items.begin() + active_index);
|
||||
active_index = 0;
|
||||
while (m_style_items.empty() || !load_style(active_index))
|
||||
m_style_items.erase(m_style_items.begin());
|
||||
// no one style from config is loadable
|
||||
if (m_style_items.empty()) {
|
||||
@ -61,14 +61,14 @@ void StyleManager::init(AppConfig *app_config, const EmbossStyles &default_style
|
||||
m_style_items.push_back({std::move(style)});
|
||||
}
|
||||
// try to load first default font
|
||||
[[maybe_unused]] bool loaded = load_style(activ_index);
|
||||
[[maybe_unused]] bool loaded = load_style(active_index);
|
||||
assert(loaded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool StyleManager::store_styles_to_app_config(bool use_modification,
|
||||
bool store_activ_index)
|
||||
bool store_active_index)
|
||||
{
|
||||
assert(m_app_config != nullptr);
|
||||
if (m_app_config == nullptr) return false;
|
||||
@ -87,7 +87,7 @@ bool StyleManager::store_styles_to_app_config(bool use_modification,
|
||||
m_style_cache.stored_wx_font = m_style_cache.wx_font;
|
||||
}
|
||||
|
||||
if (store_activ_index)
|
||||
if (store_active_index)
|
||||
{
|
||||
size_t style_index = exist_stored_style() ?
|
||||
m_style_cache.style_index :
|
||||
@ -200,7 +200,7 @@ bool StyleManager::load_style(const EmbossStyle &style, const wxFont &font)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StyleManager::is_activ_font() { return m_style_cache.font_file.has_value(); }
|
||||
bool StyleManager::is_active_font() { return m_style_cache.font_file.has_value(); }
|
||||
|
||||
bool StyleManager::load_first_valid_font() {
|
||||
while (!m_style_items.empty()) {
|
||||
@ -228,7 +228,7 @@ void StyleManager::clear_imgui_font() { m_style_cache.atlas.Clear(); }
|
||||
|
||||
ImFont *StyleManager::get_imgui_font()
|
||||
{
|
||||
if (!is_activ_font()) return nullptr;
|
||||
if (!is_active_font()) return nullptr;
|
||||
|
||||
ImVector<ImFont *> &fonts = m_style_cache.atlas.Fonts;
|
||||
if (fonts.empty()) return nullptr;
|
||||
|
@ -46,7 +46,7 @@ public:
|
||||
/// <param name="use_modification">When true cache state will be used for store</param>
|
||||
/// <param name="use_modification">When true store activ index into configuration</param>
|
||||
/// <returns>True on succes otherwise False.</returns>
|
||||
bool store_styles_to_app_config(bool use_modification = true, bool store_activ_index = true);
|
||||
bool store_styles_to_app_config(bool use_modification = true, bool store_active_index = true);
|
||||
|
||||
/// <summary>
|
||||
/// Append actual style to style list
|
||||
@ -185,7 +185,7 @@ public:
|
||||
};
|
||||
|
||||
// check if exist selected font style in manager
|
||||
bool is_activ_font();
|
||||
bool is_active_font();
|
||||
|
||||
// Limits for imgui loaded font size
|
||||
// Value out of limits is crop
|
||||
|
@ -19,7 +19,7 @@ const std::string EmbossStylesSerializable::APP_CONFIG_FONT_COLLECTION = "colle
|
||||
const std::string EmbossStylesSerializable::APP_CONFIG_FONT_CHAR_GAP = "char_gap";
|
||||
const std::string EmbossStylesSerializable::APP_CONFIG_FONT_LINE_GAP = "line_gap";
|
||||
|
||||
const std::string EmbossStylesSerializable::APP_CONFIG_ACTIVE_FONT = "activ_font";
|
||||
const std::string EmbossStylesSerializable::APP_CONFIG_ACTIVE_FONT = "active_font";
|
||||
|
||||
std::string EmbossStylesSerializable::create_section_name(unsigned index)
|
||||
{
|
||||
@ -150,8 +150,8 @@ void EmbossStylesSerializable::store_style_index(AppConfig &cfg, unsigned index)
|
||||
// store actual font index
|
||||
cfg.clear_section(AppConfig::SECTION_EMBOSS_STYLE);
|
||||
// activ font first index is +1 to correspond with section name
|
||||
std::string activ_font = std::to_string(index);
|
||||
cfg.set(AppConfig::SECTION_EMBOSS_STYLE, APP_CONFIG_ACTIVE_FONT, activ_font);
|
||||
std::string active_font = std::to_string(index);
|
||||
cfg.set(AppConfig::SECTION_EMBOSS_STYLE, APP_CONFIG_ACTIVE_FONT, active_font);
|
||||
}
|
||||
|
||||
std::optional<size_t> EmbossStylesSerializable::load_style_index(const AppConfig &cfg)
|
||||
|
@ -220,10 +220,16 @@ SCENARIO( "make_xxx functions produce meshes.") {
|
||||
GIVEN("make_sphere() function") {
|
||||
WHEN("make_sphere() is called with arguments 10, PI / 3") {
|
||||
TriangleMesh sph = make_sphere(10, PI / 243.0);
|
||||
THEN("Resulting mesh has one point at 0,0,-10 and one at 0,0,10") {
|
||||
const std::vector<stl_vertex> &verts = sph.its.vertices;
|
||||
REQUIRE(std::count_if(verts.begin(), verts.end(), [](const Vec3f& t) { return is_approx(t, Vec3f(0.f, 0.f, 10.f)); } ) == 1);
|
||||
REQUIRE(std::count_if(verts.begin(), verts.end(), [](const Vec3f& t) { return is_approx(t, Vec3f(0.f, 0.f, -10.f)); } ) == 1);
|
||||
THEN( "Edge length is smaller than limit but not smaller than half of it") {
|
||||
double len = (sph.its.vertices[sph.its.indices[0][0]] - sph.its.vertices[sph.its.indices[0][1]]).norm();
|
||||
double limit = 10*PI/243.;
|
||||
REQUIRE(len <= limit);
|
||||
REQUIRE(len >= limit/2.);
|
||||
}
|
||||
THEN( "Vertices are about the correct distance from the origin") {
|
||||
bool all_vertices_ok = std::all_of(sph.its.vertices.begin(), sph.its.vertices.end(),
|
||||
[](const stl_vertex& pt) { return is_approx(pt.squaredNorm(), 100.f); });
|
||||
REQUIRE(all_vertices_ok);
|
||||
}
|
||||
THEN( "The mesh volume is approximately 4/3 * pi * 10^3") {
|
||||
REQUIRE(abs(sph.volume() - (4.0/3.0 * M_PI * std::pow(10,3))) < 1); // 1% tolerance?
|
||||
|
@ -201,7 +201,7 @@ ExPolygons heal_and_check(const Polygons &polygons)
|
||||
{
|
||||
Pointfs intersections_prev = intersection_points(polygons);
|
||||
Points polygons_points = to_points(polygons);
|
||||
Points duplicits_prev = collect_duplications(polygons_points);
|
||||
Points duplicits_prev = collect_duplicates(polygons_points);
|
||||
|
||||
ExPolygons shape = Emboss::heal_shape(polygons);
|
||||
|
||||
@ -215,7 +215,7 @@ ExPolygons heal_and_check(const Polygons &polygons)
|
||||
|
||||
Pointfs intersections = intersection_points(shape);
|
||||
Points shape_points = to_points(shape);
|
||||
Points duplicits = collect_duplications(shape_points);
|
||||
Points duplicits = collect_duplicates(shape_points);
|
||||
//{
|
||||
// BoundingBox bb(polygons_points);
|
||||
// // bb.scale(svg_scale);
|
||||
|
Loading…
x
Reference in New Issue
Block a user