Measure: Initial porting of Measure Gizmo

This commit is contained in:
enricoturri1966 2023-10-31 11:43:04 +08:00 committed by Noisyfox
parent 1561d65712
commit f72d42f920
31 changed files with 5276 additions and 146 deletions

View file

@ -0,0 +1,37 @@
<?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 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
<g id="copy">
<g>
<path fill="#ED6B21" d="M115.76,51.2l-8.06-8.06c-2.47-2.47-6.97-4.34-10.47-4.34h-50.8c-4.2,0-7.62,3.42-7.62,7.62v66.04
c0,4.2,3.42,7.62,7.62,7.62h66.04c4.2,0,7.62-3.42,7.62-7.62v-50.8C120.09,58.17,118.23,53.67,115.76,51.2z M111.42,54.04h-6.57
v-6.57L111.42,54.04z M115.01,112.47c0,1.4-1.14,2.54-2.54,2.54H46.43c-1.4,0-2.54-1.14-2.54-2.54V46.42
c0-1.4,1.14-2.54,2.54-2.54h50.8c0.74,0,1.63,0.18,2.54,0.46v12.24c0,1.4,1.14,2.54,2.54,2.54h12.24c0.28,0.91,0.46,1.8,0.46,2.54
V112.47z"/>
<path fill="#ED6B21" d="M53.97,59.13h35.72c1.4,0,2.54-1.14,2.54-2.54s-1.14-2.54-2.54-2.54H53.97c-1.4,0-2.54,1.14-2.54,2.54
S52.56,59.13,53.97,59.13z"/>
<path fill="#ED6B21" d="M104.93,69.29H53.97c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h50.96c1.4,0,2.54-1.14,2.54-2.54
S106.33,69.29,104.93,69.29z"/>
<path fill="#ED6B21" d="M104.93,84.53H53.97c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h50.96c1.4,0,2.54-1.14,2.54-2.54
S106.33,84.53,104.93,84.53z"/>
<path fill="#ED6B21" d="M104.93,99.77H53.97c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h50.96c1.4,0,2.54-1.14,2.54-2.54
S106.33,99.77,104.93,99.77z"/>
</g>
<g>
<path fill="#808080" d="M85.27,20.71l-8.06-8.06c-2.47-2.47-6.97-4.34-10.47-4.34h-50.8c-4.2,0-7.62,3.42-7.62,7.62v66.04
c0,4.2,3.42,7.62,7.62,7.62h17.78c1.4,0,2.54-1.14,2.54-2.54s-1.14-2.54-2.54-2.54H15.94c-1.4,0-2.54-1.14-2.54-2.54V15.94
c0-1.4,1.14-2.54,2.54-2.54h50.8c0.74,0,1.63,0.18,2.54,0.46V26.1c0,1.4,1.14,2.54,2.54,2.54h12.45c0.16,0.49,0.25,0.93,0.25,1.27
v3.81c0,1.4,1.14,2.54,2.54,2.54c1.4,0,2.54-1.14,2.54-2.54v-3.81C89.61,27.14,87.75,23.19,85.27,20.71z M74.37,16.99l6.57,6.57
h-6.57V16.99z"/>
<path fill="#808080" d="M59.21,23.56H23.48c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h35.72c1.4,0,2.54-1.14,2.54-2.54
S60.61,23.56,59.21,23.56z"/>
<path fill="#808080" d="M28.73,38.8h-5.24c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h5.24c1.4,0,2.54-1.14,2.54-2.54
S30.13,38.8,28.73,38.8z"/>
<path fill="#808080" d="M28.73,54.04h-5.24c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h5.24c1.4,0,2.54-1.14,2.54-2.54
S30.13,54.04,28.73,54.04z"/>
<path fill="#808080" d="M28.73,69.29h-5.24c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h5.24c1.4,0,2.54-1.14,2.54-2.54
S30.13,69.29,28.73,69.29z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 23.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.0"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 128 128"
enable-background="new 0 0 128 128"
xml:space="preserve"
sodipodi:docname="measure2.svg"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs976">
</defs><sodipodi:namedview
id="namedview974"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="6.34375"
inkscape:cx="63.921182"
inkscape:cy="64.078818"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="3191"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<g
id="g1872"><path
fill="#ed6b21"
d="m 26.966044,54.457929 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494893,0.681318 1.48862,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
id="path958-8"
style="fill:#ed6b21;fill-opacity:1;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#ed6b21"
d="m 36.966044,54.458378 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494893,0.681318 1.48862,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
id="path958-8-1"
style="fill:#ed6b21;fill-opacity:1;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#ed6b21"
d="m 46.966202,60.202938 0.07558,-14.999888 c 0.0063,-1.244993 0.681318,-2.242399 1.511294,-2.232988 0.829976,0.0087 1.494893,1.021998 1.48862,2.266999 l -0.07558,14.999887 c -0.0063,1.244995 -0.681318,2.242401 -1.511294,2.232988 -0.829976,-0.0087 -1.494893,-1.021997 -1.48862,-2.266998 z"
id="path958-8-9"
style="fill:#ed6b21;fill-opacity:1;stroke-width:1.22476;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#ed6b21"
d="m 56.966044,54.458378 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494893,0.681318 1.48862,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
id="path958-8-9-3"
style="fill:#ed6b21;fill-opacity:1;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#ed6b21"
d="m 66.966044,54.458378 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494893,0.681318 1.48862,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
id="path958-8-9-3-0"
style="fill:#ed6b21;fill-opacity:1;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#ed6b21"
d="m 76.966202,60.202932 0.07558,-14.999881 c 0.0063,-1.244994 0.681318,-2.2424 1.511294,-2.23299 0.829976,0.0085 1.494893,1.021998 1.48862,2.266999 l -0.07558,14.999882 c -0.0063,1.244995 -0.681318,2.2424 -1.511294,2.232987 -0.829976,-0.0085 -1.494893,-1.021996 -1.48862,-2.266997 z"
id="path958-8-9-3-1"
style="fill:#ed6b21;fill-opacity:1;stroke-width:1.22474;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#ed6b21"
d="m 86.966044,54.458378 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494893,0.681318 1.48862,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
id="path958-8-9-3-1-4"
style="fill:#ed6b21;fill-opacity:1;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#ed6b21"
d="m 96.966044,54.458378 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494892,0.681318 1.488622,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
id="path958-8-9-3-1-4-2"
style="fill:#ed6b21;fill-opacity:1;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /></g><g
id="g964"
transform="translate(0,34.9)">
<path
fill="#ffffff"
d="M 108.79,51.6 H 19.21 c -1.93,0 -3.5,-1.57 -3.5,-3.5 V 10.12 c 0,-1.93 1.57,-3.5 3.5,-3.5 h 89.57 c 1.93,0 3.5,1.57 3.5,3.5 V 48.1 c 0.01,1.93 -1.57,3.5 -3.49,3.5 z M 19.21,9.62 c -0.27,0 -0.5,0.23 -0.5,0.5 V 48.1 c 0,0.27 0.23,0.5 0.5,0.5 h 89.57 c 0.27,0 0.5,-0.23 0.5,-0.5 V 10.12 c 0,-0.27 -0.23,-0.5 -0.5,-0.5 z"
id="path962" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

View file

@ -161,6 +161,7 @@ namespace ImGui
const wchar_t ClippyMarker = 0x0802; const wchar_t ClippyMarker = 0x0802;
const wchar_t InfoMarker = 0x0803; const wchar_t InfoMarker = 0x0803;
const wchar_t SliderFloatEditBtnIcon = 0x0804; const wchar_t SliderFloatEditBtnIcon = 0x0804;
const wchar_t ClipboardBtnIcon = 0x0805;
// BBS // BBS
const wchar_t CircleButtonIcon = 0x0810; const wchar_t CircleButtonIcon = 0x0810;

View file

@ -208,6 +208,9 @@ set(lisbslic3r_sources
ModelArrange.cpp ModelArrange.cpp
MultiMaterialSegmentation.cpp MultiMaterialSegmentation.cpp
MultiMaterialSegmentation.hpp MultiMaterialSegmentation.hpp
Measure.hpp
Measure.cpp
MeasureUtils.hpp
CustomGCode.cpp CustomGCode.cpp
CustomGCode.hpp CustomGCode.hpp
Arrange.hpp Arrange.hpp
@ -307,6 +310,7 @@ set(lisbslic3r_sources
Surface.hpp Surface.hpp
SurfaceCollection.cpp SurfaceCollection.cpp
SurfaceCollection.hpp SurfaceCollection.hpp
SurfaceMesh.hpp
SVG.cpp SVG.cpp
SVG.hpp SVG.hpp
Technologies.hpp Technologies.hpp

View file

@ -1,3 +1,18 @@
///|/ Copyright (c) Prusa Research 2016 - 2023 Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, Lukáš Hejl @hejllukas
///|/ Copyright (c) 2017 Eyal Soha @eyal0
///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel
///|/
///|/ ported from lib/Slic3r/Geometry.pm:
///|/ Copyright (c) Prusa Research 2017 - 2022 Vojtěch Bubník @bubnikv
///|/ Copyright (c) Slic3r 2011 - 2015 Alessandro Ranellucci @alranel
///|/ Copyright (c) 2013 Jose Luis Perez Diez
///|/ Copyright (c) 2013 Anders Sundman
///|/ Copyright (c) 2013 Jesse Vincent
///|/ Copyright (c) 2012 Mike Sheldrake @mesheldrake
///|/ Copyright (c) 2012 Mark Hindess
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_Geometry_hpp_ #ifndef slic3r_Geometry_hpp_
#define slic3r_Geometry_hpp_ #define slic3r_Geometry_hpp_

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2021 - 2022 Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "Circle.hpp" #include "Circle.hpp"
#include "../Polygon.hpp" #include "../Polygon.hpp"
@ -108,7 +112,7 @@ Circled circle_taubin_newton(const Vec2ds& input, size_t cycles)
return out; return out;
} }
Circled circle_ransac(const Vec2ds& input, size_t iterations) Circled circle_ransac(const Vec2ds& input, size_t iterations, double* min_error)
{ {
if (input.size() < 3) if (input.size() < 3)
return Circled::make_invalid(); return Circled::make_invalid();
@ -132,6 +136,8 @@ Circled circle_ransac(const Vec2ds& input, size_t iterations)
circle_best = c; circle_best = c;
} }
} }
if (min_error)
*min_error = err_min;
return circle_best; return circle_best;
} }

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2021 - 2022 Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_Geometry_Circle_hpp_ #ifndef slic3r_Geometry_Circle_hpp_
#define slic3r_Geometry_Circle_hpp_ #define slic3r_Geometry_Circle_hpp_
@ -102,7 +106,7 @@ inline Vec2d circle_center_taubin_newton(const Vec2ds& input, size_t cycles = 20
Circled circle_taubin_newton(const Vec2ds& input, size_t cycles = 20); Circled circle_taubin_newton(const Vec2ds& input, size_t cycles = 20);
// Find circle using RANSAC randomized algorithm. // Find circle using RANSAC randomized algorithm.
Circled circle_ransac(const Vec2ds& input, size_t iterations = 20); Circled circle_ransac(const Vec2ds& input, size_t iterations = 20, double* min_error = nullptr);
// Randomized algorithm by Emo Welzl, working with squared radii for efficiency. The returned circle radius is inflated by epsilon. // Randomized algorithm by Emo Welzl, working with squared radii for efficiency. The returned circle radius is inflated by epsilon.
template<typename Vector, typename Points> template<typename Vector, typename Points>

1255
src/libslic3r/Measure.cpp Normal file

File diff suppressed because it is too large Load diff

200
src/libslic3r/Measure.hpp Normal file
View file

@ -0,0 +1,200 @@
///|/ Copyright (c) Prusa Research 2022 - 2023 Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef Slic3r_Measure_hpp_
#define Slic3r_Measure_hpp_
#include <optional>
#include <memory>
#include "Point.hpp"
struct indexed_triangle_set;
namespace Slic3r {
class TriangleMesh;
namespace Measure {
enum class SurfaceFeatureType : int {
Undef = 0,
Point = 1 << 0,
Edge = 1 << 1,
Circle = 1 << 2,
Plane = 1 << 3
};
class SurfaceFeature {
public:
SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional<Vec3d> pt3 = std::nullopt, double value = 0.0)
: m_type(type), m_pt1(pt1), m_pt2(pt2), m_pt3(pt3), m_value(value) {}
explicit SurfaceFeature(const Vec3d& pt)
: m_type{SurfaceFeatureType::Point}, m_pt1{pt} {}
// Get type of this feature.
SurfaceFeatureType get_type() const { return m_type; }
// For points, return the point.
Vec3d get_point() const { assert(m_type == SurfaceFeatureType::Point); return m_pt1; }
// For edges, return start and end.
std::pair<Vec3d, Vec3d> get_edge() const { assert(m_type == SurfaceFeatureType::Edge); return std::make_pair(m_pt1, m_pt2); }
// For circles, return center, radius and normal.
std::tuple<Vec3d, double, Vec3d> get_circle() const { assert(m_type == SurfaceFeatureType::Circle); return std::make_tuple(m_pt1, m_value, m_pt2); }
// For planes, return index into vector provided by Measuring::get_plane_triangle_indices, normal and point.
std::tuple<int, Vec3d, Vec3d> get_plane() const { assert(m_type == SurfaceFeatureType::Plane); return std::make_tuple(int(m_value), m_pt1, m_pt2); }
// For anything, return an extra point that should also be considered a part of this.
std::optional<Vec3d> get_extra_point() const { assert(m_type != SurfaceFeatureType::Undef); return m_pt3; }
bool operator == (const SurfaceFeature& other) const {
if (this->m_type != other.m_type) return false;
switch (this->m_type)
{
case SurfaceFeatureType::Undef: { break; }
case SurfaceFeatureType::Point: { return (this->m_pt1.isApprox(other.m_pt1)); }
case SurfaceFeatureType::Edge: {
return (this->m_pt1.isApprox(other.m_pt1) && this->m_pt2.isApprox(other.m_pt2)) ||
(this->m_pt1.isApprox(other.m_pt2) && this->m_pt2.isApprox(other.m_pt1));
}
case SurfaceFeatureType::Plane:
case SurfaceFeatureType::Circle: {
return (this->m_pt1.isApprox(other.m_pt1) && this->m_pt2.isApprox(other.m_pt2) && std::abs(this->m_value - other.m_value) < EPSILON);
}
}
return false;
}
bool operator != (const SurfaceFeature& other) const {
return !operator == (other);
}
private:
SurfaceFeatureType m_type{ SurfaceFeatureType::Undef };
Vec3d m_pt1{ Vec3d::Zero() };
Vec3d m_pt2{ Vec3d::Zero() };
std::optional<Vec3d> m_pt3;
double m_value{ 0.0 };
};
class MeasuringImpl;
class Measuring {
public:
// Construct the measurement object on a given its.
explicit Measuring(const indexed_triangle_set& its);
~Measuring();
// Given a face_idx where the mouse cursor points, return a feature that
// should be highlighted (if any).
std::optional<SurfaceFeature> get_feature(size_t face_idx, const Vec3d& point) const;
// Return total number of planes.
int get_num_of_planes() const;
// Returns a list of triangle indices for given plane.
const std::vector<int>& get_plane_triangle_indices(int idx) const;
// Returns the surface features of the plane with the given index
const std::vector<SurfaceFeature>& get_plane_features(unsigned int plane_id) const;
// Returns the mesh used for measuring
const indexed_triangle_set& get_its() const;
private:
std::unique_ptr<MeasuringImpl> priv;
};
struct DistAndPoints {
DistAndPoints(double dist_, Vec3d from_, Vec3d to_) : dist(dist_), from(from_), to(to_) {}
double dist;
Vec3d from;
Vec3d to;
};
struct AngleAndEdges {
AngleAndEdges(double angle_, const Vec3d& center_, const std::pair<Vec3d, Vec3d>& e1_, const std::pair<Vec3d, Vec3d>& e2_, double radius_, bool coplanar_)
: angle(angle_), center(center_), e1(e1_), e2(e2_), radius(radius_), coplanar(coplanar_) {}
double angle;
Vec3d center;
std::pair<Vec3d, Vec3d> e1;
std::pair<Vec3d, Vec3d> e2;
double radius;
bool coplanar;
static const AngleAndEdges Dummy;
};
struct MeasurementResult {
std::optional<AngleAndEdges> angle;
std::optional<DistAndPoints> distance_infinite;
std::optional<DistAndPoints> distance_strict;
std::optional<Vec3d> distance_xyz;
bool has_distance_data() const {
return distance_infinite.has_value() || distance_strict.has_value();
}
bool has_any_data() const {
return angle.has_value() || distance_infinite.has_value() || distance_strict.has_value() || distance_xyz.has_value();
}
};
// Returns distance/angle between two SurfaceFeatures.
MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b, const Measuring* measuring = nullptr);
inline Vec3d edge_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); }
inline Vec3d edge_direction(const std::pair<Vec3d, Vec3d>& e) { return edge_direction(e.first, e.second); }
inline Vec3d edge_direction(const SurfaceFeature& edge) {
assert(edge.get_type() == SurfaceFeatureType::Edge);
return edge_direction(edge.get_edge());
}
inline Vec3d plane_normal(const SurfaceFeature& plane) {
assert(plane.get_type() == SurfaceFeatureType::Plane);
return std::get<1>(plane.get_plane());
}
inline bool are_parallel(const Vec3d& v1, const Vec3d& v2) { return std::abs(std::abs(v1.dot(v2)) - 1.0) < EPSILON; }
inline bool are_perpendicular(const Vec3d& v1, const Vec3d& v2) { return std::abs(v1.dot(v2)) < EPSILON; }
inline bool are_parallel(const std::pair<Vec3d, Vec3d>& e1, const std::pair<Vec3d, Vec3d>& e2) {
return are_parallel(e1.second - e1.first, e2.second - e2.first);
}
inline bool are_parallel(const SurfaceFeature& f1, const SurfaceFeature& f2) {
if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Edge)
return are_parallel(edge_direction(f1), edge_direction(f2));
else if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Plane)
return are_perpendicular(edge_direction(f1), plane_normal(f2));
else
return false;
}
inline bool are_perpendicular(const SurfaceFeature& f1, const SurfaceFeature& f2) {
if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Edge)
return are_perpendicular(edge_direction(f1), edge_direction(f2));
else if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Plane)
return are_parallel(edge_direction(f1), plane_normal(f2));
else
return false;
}
} // namespace Measure
} // namespace Slic3r
#endif // Slic3r_Measure_hpp_

View file

@ -0,0 +1,390 @@
///|/ Copyright (c) Prusa Research 2022 Enrico Turri @enricoturri1966
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef Slic3r_MeasureUtils_hpp_
#define Slic3r_MeasureUtils_hpp_
#include <initializer_list>
namespace Slic3r {
namespace Measure {
// Utility class used to calculate distance circle-circle
// Adaptation of code found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Polynomial1.h
class Polynomial1
{
public:
Polynomial1(std::initializer_list<double> values)
{
// C++ 11 will call the default constructor for
// Polynomial1<Real> p{}, so it is guaranteed that
// values.size() > 0.
m_coefficient.resize(values.size());
std::copy(values.begin(), values.end(), m_coefficient.begin());
EliminateLeadingZeros();
}
// Construction and destruction. The first constructor creates a
// polynomial of the specified degree but sets all coefficients to
// zero (to ensure initialization). You are responsible for setting
// the coefficients, presumably with the degree-term set to a nonzero
// number. In the second constructor, the degree is the number of
// initializers plus 1, but then adjusted so that coefficient[degree]
// is not zero (unless all initializer values are zero).
explicit Polynomial1(uint32_t degree)
: m_coefficient(static_cast<size_t>(degree) + 1, 0.0)
{}
// Eliminate any leading zeros in the polynomial, except in the case
// the degree is 0 and the coefficient is 0. The elimination is
// necessary when arithmetic operations cause a decrease in the degree
// of the result. For example, (1 + x + x^2) + (1 + 2*x - x^2) =
// (2 + 3*x). The inputs both have degree 2, so the result is created
// with degree 2. After the addition we find that the degree is in
// fact 1 and resize the array of coefficients. This function is
// called internally by the arithmetic operators, but it is exposed in
// the public interface in case you need it for your own purposes.
void EliminateLeadingZeros()
{
const size_t size = m_coefficient.size();
if (size > 1) {
const double zero = 0.0;
int32_t leading;
for (leading = static_cast<int32_t>(size) - 1; leading > 0; --leading) {
if (m_coefficient[leading] != zero)
break;
}
m_coefficient.resize(++leading);
}
}
// Set all coefficients to the specified value.
void SetCoefficients(double value)
{
std::fill(m_coefficient.begin(), m_coefficient.end(), value);
}
inline uint32_t GetDegree() const
{
// By design, m_coefficient.size() > 0.
return static_cast<uint32_t>(m_coefficient.size() - 1);
}
inline const double& operator[](uint32_t i) const { return m_coefficient[i]; }
inline double& operator[](uint32_t i) { return m_coefficient[i]; }
// Evaluate the polynomial. If the polynomial is invalid, the
// function returns zero.
double operator()(double t) const
{
int32_t i = static_cast<int32_t>(m_coefficient.size());
double result = m_coefficient[--i];
for (--i; i >= 0; --i) {
result *= t;
result += m_coefficient[i];
}
return result;
}
protected:
// The class is designed so that m_coefficient.size() >= 1.
std::vector<double> m_coefficient;
};
inline Polynomial1 operator * (const Polynomial1& p0, const Polynomial1& p1)
{
const uint32_t p0Degree = p0.GetDegree();
const uint32_t p1Degree = p1.GetDegree();
Polynomial1 result(p0Degree + p1Degree);
result.SetCoefficients(0.0);
for (uint32_t i0 = 0; i0 <= p0Degree; ++i0) {
for (uint32_t i1 = 0; i1 <= p1Degree; ++i1) {
result[i0 + i1] += p0[i0] * p1[i1];
}
}
return result;
}
inline Polynomial1 operator + (const Polynomial1& p0, const Polynomial1& p1)
{
const uint32_t p0Degree = p0.GetDegree();
const uint32_t p1Degree = p1.GetDegree();
uint32_t i;
if (p0Degree >= p1Degree) {
Polynomial1 result(p0Degree);
for (i = 0; i <= p1Degree; ++i) {
result[i] = p0[i] + p1[i];
}
for (/**/; i <= p0Degree; ++i) {
result[i] = p0[i];
}
result.EliminateLeadingZeros();
return result;
}
else {
Polynomial1 result(p1Degree);
for (i = 0; i <= p0Degree; ++i) {
result[i] = p0[i] + p1[i];
}
for (/**/; i <= p1Degree; ++i) {
result[i] = p1[i];
}
result.EliminateLeadingZeros();
return result;
}
}
inline Polynomial1 operator - (const Polynomial1& p0, const Polynomial1& p1)
{
const uint32_t p0Degree = p0.GetDegree();
const uint32_t p1Degree = p1.GetDegree();
uint32_t i;
if (p0Degree >= p1Degree) {
Polynomial1 result(p0Degree);
for (i = 0; i <= p1Degree; ++i) {
result[i] = p0[i] - p1[i];
}
for (/**/; i <= p0Degree; ++i) {
result[i] = p0[i];
}
result.EliminateLeadingZeros();
return result;
}
else {
Polynomial1 result(p1Degree);
for (i = 0; i <= p0Degree; ++i) {
result[i] = p0[i] - p1[i];
}
for (/**/; i <= p1Degree; ++i) {
result[i] = -p1[i];
}
result.EliminateLeadingZeros();
return result;
}
}
inline Polynomial1 operator * (double scalar, const Polynomial1& p)
{
const uint32_t degree = p.GetDegree();
Polynomial1 result(degree);
for (uint32_t i = 0; i <= degree; ++i) {
result[i] = scalar * p[i];
}
return result;
}
// Utility class used to calculate distance circle-circle
// Adaptation of code found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/RootsPolynomial.h
class RootsPolynomial
{
public:
// General equations: sum_{i=0}^{d} c(i)*t^i = 0. The input array 'c'
// must have at least d+1 elements and the output array 'root' must
// have at least d elements.
// Find the roots on (-infinity,+infinity).
static int32_t Find(int32_t degree, const double* c, uint32_t maxIterations, double* roots)
{
if (degree >= 0 && c != nullptr) {
const double zero = 0.0;
while (degree >= 0 && c[degree] == zero) {
--degree;
}
if (degree > 0) {
// Compute the Cauchy bound.
const double one = 1.0;
const double invLeading = one / c[degree];
double maxValue = zero;
for (int32_t i = 0; i < degree; ++i) {
const double value = std::fabs(c[i] * invLeading);
if (value > maxValue)
maxValue = value;
}
const double bound = one + maxValue;
return FindRecursive(degree, c, -bound, bound, maxIterations, roots);
}
else if (degree == 0)
// The polynomial is a nonzero constant.
return 0;
else {
// The polynomial is identically zero.
roots[0] = zero;
return 1;
}
}
else
// Invalid degree or c.
return 0;
}
// If you know that p(tmin) * p(tmax) <= 0, then there must be at
// least one root in [tmin, tmax]. Compute it using bisection.
static bool Find(int32_t degree, const double* c, double tmin, double tmax, uint32_t maxIterations, double& root)
{
const double zero = 0.0;
double pmin = Evaluate(degree, c, tmin);
if (pmin == zero) {
root = tmin;
return true;
}
double pmax = Evaluate(degree, c, tmax);
if (pmax == zero) {
root = tmax;
return true;
}
if (pmin * pmax > zero)
// It is not known whether the interval bounds a root.
return false;
if (tmin >= tmax)
// Invalid ordering of interval endpoitns.
return false;
for (uint32_t i = 1; i <= maxIterations; ++i) {
root = 0.5 * (tmin + tmax);
// This test is designed for 'float' or 'double' when tmin
// and tmax are consecutive floating-point numbers.
if (root == tmin || root == tmax)
break;
const double p = Evaluate(degree, c, root);
const double product = p * pmin;
if (product < zero) {
tmax = root;
pmax = p;
}
else if (product > zero) {
tmin = root;
pmin = p;
}
else
break;
}
return true;
}
// Support for the Find functions.
static int32_t FindRecursive(int32_t degree, double const* c, double tmin, double tmax, uint32_t maxIterations, double* roots)
{
// The base of the recursion.
const double zero = 0.0;
double root = zero;
if (degree == 1) {
int32_t numRoots;
if (c[1] != zero) {
root = -c[0] / c[1];
numRoots = 1;
}
else if (c[0] == zero) {
root = zero;
numRoots = 1;
}
else
numRoots = 0;
if (numRoots > 0 && tmin <= root && root <= tmax) {
roots[0] = root;
return 1;
}
return 0;
}
// Find the roots of the derivative polynomial scaled by 1/degree.
// The scaling avoids the factorial growth in the coefficients;
// for example, without the scaling, the high-order term x^d
// becomes (d!)*x through multiple differentiations. With the
// scaling we instead get x. This leads to better numerical
// behavior of the root finder.
const int32_t derivDegree = degree - 1;
std::vector<double> derivCoeff(static_cast<size_t>(derivDegree) + 1);
std::vector<double> derivRoots(derivDegree);
for (int32_t i = 0, ip1 = 1; i <= derivDegree; ++i, ++ip1) {
derivCoeff[i] = c[ip1] * (double)(ip1) / (double)degree;
}
const int32_t numDerivRoots = FindRecursive(degree - 1, &derivCoeff[0], tmin, tmax, maxIterations, &derivRoots[0]);
int32_t numRoots = 0;
if (numDerivRoots > 0) {
// Find root on [tmin,derivRoots[0]].
if (Find(degree, c, tmin, derivRoots[0], maxIterations, root))
roots[numRoots++] = root;
// Find root on [derivRoots[i],derivRoots[i+1]].
for (int32_t i = 0, ip1 = 1; i <= numDerivRoots - 2; ++i, ++ip1) {
if (Find(degree, c, derivRoots[i], derivRoots[ip1], maxIterations, root))
roots[numRoots++] = root;
}
// Find root on [derivRoots[numDerivRoots-1],tmax].
if (Find(degree, c, derivRoots[static_cast<size_t>(numDerivRoots) - 1], tmax, maxIterations, root))
roots[numRoots++] = root;
}
else {
// The polynomial is monotone on [tmin,tmax], so has at most one root.
if (Find(degree, c, tmin, tmax, maxIterations, root))
roots[numRoots++] = root;
}
return numRoots;
}
static double Evaluate(int32_t degree, const double* c, double t)
{
int32_t i = degree;
double result = c[i];
while (--i >= 0) {
result = t * result + c[i];
}
return result;
}
};
// Adaptation of code found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Vector.h
// Construct a single vector orthogonal to the nonzero input vector. If
// the maximum absolute component occurs at index i, then the orthogonal
// vector U has u[i] = v[i+1], u[i+1] = -v[i], and all other components
// zero. The index addition i+1 is computed modulo N.
inline Vec3d get_orthogonal(const Vec3d& v, bool unitLength)
{
double cmax = std::fabs(v[0]);
int32_t imax = 0;
for (int32_t i = 1; i < 3; ++i) {
double c = std::fabs(v[i]);
if (c > cmax) {
cmax = c;
imax = i;
}
}
Vec3d result = Vec3d::Zero();
int32_t inext = imax + 1;
if (inext == 3)
inext = 0;
result[imax] = v[inext];
result[inext] = -v[imax];
if (unitLength) {
const double sqrDistance = result[imax] * result[imax] + result[inext] * result[inext];
const double invLength = 1.0 / std::sqrt(sqrDistance);
result[imax] *= invLength;
result[inext] *= invLength;
}
return result;
}
} // namespace Slic3r
} // namespace Measure
#endif // Slic3r_MeasureUtils_hpp_

View file

@ -0,0 +1,167 @@
///|/ Copyright (c) Prusa Research 2022 Lukáš Matěna @lukasmatena
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_SurfaceMesh_hpp_
#define slic3r_SurfaceMesh_hpp_
#include <admesh/stl.h>
#include <libslic3r/TriangleMesh.hpp>
#include "boost/container/small_vector.hpp"
namespace Slic3r {
class TriangleMesh;
enum Face_index : int;
class Halfedge_index {
friend class SurfaceMesh;
public:
Halfedge_index() : m_face(Face_index(-1)), m_side(0) {}
Face_index face() const { return m_face; }
unsigned char side() const { return m_side; }
bool is_invalid() const { return int(m_face) < 0; }
bool operator!=(const Halfedge_index& rhs) const { return ! ((*this) == rhs); }
bool operator==(const Halfedge_index& rhs) const { return m_face == rhs.m_face && m_side == rhs.m_side; }
private:
Halfedge_index(int face_idx, unsigned char side_idx) : m_face(Face_index(face_idx)), m_side(side_idx) {}
Face_index m_face;
unsigned char m_side;
};
class Vertex_index {
friend class SurfaceMesh;
public:
Vertex_index() : m_face(Face_index(-1)), m_vertex_idx(0) {}
bool is_invalid() const { return int(m_face) < 0; }
bool operator==(const Vertex_index& rhs) const = delete; // Use SurfaceMesh::is_same_vertex.
private:
Vertex_index(int face_idx, unsigned char vertex_idx) : m_face(Face_index(face_idx)), m_vertex_idx(vertex_idx) {}
Face_index m_face;
unsigned char m_vertex_idx;
};
class SurfaceMesh {
public:
explicit SurfaceMesh(const indexed_triangle_set& its)
: m_its(its),
m_face_neighbors(its_face_neighbors_par(its))
{}
SurfaceMesh(const SurfaceMesh&) = delete;
SurfaceMesh& operator=(const SurfaceMesh&) = delete;
Vertex_index source(Halfedge_index h) const { assert(! h.is_invalid()); return Vertex_index(h.m_face, h.m_side); }
Vertex_index target(Halfedge_index h) const { assert(! h.is_invalid()); return Vertex_index(h.m_face, h.m_side == 2 ? 0 : h.m_side + 1); }
Face_index face(Halfedge_index h) const { assert(! h.is_invalid()); return h.m_face; }
Halfedge_index next(Halfedge_index h) const { assert(! h.is_invalid()); h.m_side = (h.m_side + 1) % 3; return h; }
Halfedge_index prev(Halfedge_index h) const { assert(! h.is_invalid()); h.m_side = (h.m_side == 0 ? 2 : h.m_side - 1); return h; }
Halfedge_index halfedge(Vertex_index v) const { return Halfedge_index(v.m_face, (v.m_vertex_idx == 0 ? 2 : v.m_vertex_idx - 1)); }
Halfedge_index halfedge(Face_index f) const { return Halfedge_index(f, 0); }
Halfedge_index opposite(Halfedge_index h) const {
if (h.is_invalid())
return h;
int face_idx = m_face_neighbors[h.m_face][h.m_side];
Halfedge_index h_candidate = halfedge(Face_index(face_idx));
if (h_candidate.is_invalid())
return Halfedge_index(); // invalid
for (int i=0; i<3; ++i) {
if (is_same_vertex(source(h_candidate), target(h))) {
// Meshes in PrusaSlicer should be fixed enough for the following not to happen.
assert(is_same_vertex(target(h_candidate), source(h)));
return h_candidate;
}
h_candidate = next(h_candidate);
}
return Halfedge_index(); // invalid
}
Halfedge_index next_around_target(Halfedge_index h) const { return opposite(next(h)); }
Halfedge_index prev_around_target(Halfedge_index h) const { Halfedge_index op = opposite(h); return (op.is_invalid() ? Halfedge_index() : prev(op)); }
Halfedge_index next_around_source(Halfedge_index h) const { Halfedge_index op = opposite(h); return (op.is_invalid() ? Halfedge_index() : next(op)); }
Halfedge_index prev_around_source(Halfedge_index h) const { return opposite(prev(h)); }
Halfedge_index halfedge(Vertex_index source, Vertex_index target) const
{
Halfedge_index hi(source.m_face, source.m_vertex_idx);
assert(! hi.is_invalid());
const Vertex_index orig_target = this->target(hi);
Vertex_index current_target = orig_target;
while (! is_same_vertex(current_target, target)) {
hi = next_around_source(hi);
if (hi.is_invalid())
break;
current_target = this->target(hi);
if (is_same_vertex(current_target, orig_target))
return Halfedge_index(); // invalid
}
return hi;
}
const stl_vertex& point(Vertex_index v) const { return m_its.vertices[m_its.indices[v.m_face][v.m_vertex_idx]]; }
size_t degree(Vertex_index v) const
{
// In case the mesh is broken badly, the loop might end up to be infinite,
// never getting back to the first halfedge. Remember list of all half-edges
// and trip if any is encountered for the second time.
Halfedge_index h_first = halfedge(v);
boost::container::small_vector<Halfedge_index, 10> he_visited;
Halfedge_index h = next_around_target(h_first);
size_t degree = 2;
while (! h.is_invalid() && h != h_first) {
he_visited.emplace_back(h);
h = next_around_target(h);
if (std::find(he_visited.begin(), he_visited.end(), h) == he_visited.end())
return 0;
++degree;
}
return h.is_invalid() ? 0 : degree - 1;
}
size_t degree(Face_index f) const {
size_t total = 0;
for (unsigned char i=0; i<3; ++i) {
size_t d = degree(Vertex_index(f, i));
if (d == 0)
return 0;
total += d;
}
assert(total - 6 >= 0);
return total - 6; // we counted 3 halfedges from f, and one more for each neighbor
}
bool is_border(Halfedge_index h) const { return m_face_neighbors[h.m_face][h.m_side] == -1; }
bool is_same_vertex(const Vertex_index& a, const Vertex_index& b) const { return m_its.indices[a.m_face][a.m_vertex_idx] == m_its.indices[b.m_face][b.m_vertex_idx]; }
Vec3i get_face_neighbors(Face_index face_id) const { assert(int(face_id) < int(m_face_neighbors.size())); return m_face_neighbors[face_id]; }
private:
const std::vector<Vec3i> m_face_neighbors;
const indexed_triangle_set& m_its;
};
} //namespace Slic3r
#endif // slic3r_SurfaceMesh_hpp_

View file

@ -1,3 +1,12 @@
#/|/ Copyright (c) Prusa Research 2018 - 2023 Tomáš Mészáros @tamasmeszaros, David Kocík @kocikdav, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Pavel Mikuš @Godrak, Filip Sykala @Jony01, Oleksandra Iushchenko @YuSanka, Lukáš Hejl @hejllukas, Vojtěch Král @vojtechkral
#/|/ Copyright (c) 2023 Pedro Lamas @PedroLamas
#/|/ Copyright (c) 2020 Sergey Kovalev @RandoMan70
#/|/ Copyright (c) 2021 Boleslaw Ciesielski
#/|/ Copyright (c) 2019 Spencer Owen @spuder
#/|/ Copyright (c) 2019 Stephan Reichhelm @stephanr
#/|/
#/|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
#/|/
cmake_minimum_required(VERSION 3.13) cmake_minimum_required(VERSION 3.13)
project(libslic3r_gui) project(libslic3r_gui)
@ -135,6 +144,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Gizmos/GLGizmoMmuSegmentation.hpp GUI/Gizmos/GLGizmoMmuSegmentation.hpp
#GUI/Gizmos/GLGizmoFaceDetector.cpp #GUI/Gizmos/GLGizmoFaceDetector.cpp
#GUI/Gizmos/GLGizmoFaceDetector.hpp #GUI/Gizmos/GLGizmoFaceDetector.hpp
GUI/Gizmos/GLGizmoMeasure.cpp
GUI/Gizmos/GLGizmoMeasure.hpp
GUI/Gizmos/GLGizmoSeam.cpp GUI/Gizmos/GLGizmoSeam.cpp
GUI/Gizmos/GLGizmoSeam.hpp GUI/Gizmos/GLGizmoSeam.hpp
GUI/Gizmos/GLGizmoText.cpp GUI/Gizmos/GLGizmoText.cpp
@ -175,6 +186,8 @@ set(SLIC3R_GUI_SOURCES
GUI/GUI_App.hpp GUI/GUI_App.hpp
GUI/GUI_Utils.cpp GUI/GUI_Utils.cpp
GUI/GUI_Utils.hpp GUI/GUI_Utils.hpp
GUI/GUI_Geometry.cpp
GUI/GUI_Geometry.hpp
GUI/I18N.cpp GUI/I18N.cpp
GUI/I18N.hpp GUI/I18N.hpp
GUI/MainFrame.cpp GUI/MainFrame.cpp

View file

@ -1,3 +1,14 @@
///|/ Copyright (c) Prusa Research 2017 - 2023 Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Tomáš Mészáros @tamasmeszaros, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, David Kocík @kocikdav, Vojtěch Král @vojtechkral
///|/ Copyright (c) 2017 Eyal Soha @eyal0
///|/ Copyright (c) Slic3r 2015 Alessandro Ranellucci @alranel
///|/
///|/ ported from lib/Slic3r/GUI/3DScene.pm:
///|/ Copyright (c) Prusa Research 2016 - 2019 Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka
///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel
///|/ Copyright (c) 2013 Guillaume Seguin @iXce
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_3DScene_hpp_ #ifndef slic3r_3DScene_hpp_
#define slic3r_3DScene_hpp_ #define slic3r_3DScene_hpp_

View file

@ -2194,10 +2194,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
m_hover_volume_idxs.clear(); m_hover_volume_idxs.clear();
GLGizmoBase* curr_gizmo = m_gizmos.get_current();
if (curr_gizmo != nullptr)
curr_gizmo->unregister_raycasters_for_picking();
struct ModelVolumeState { struct ModelVolumeState {
ModelVolumeState(const GLVolume* volume) : ModelVolumeState(const GLVolume* volume) :
model_volume(nullptr), geometry_id(volume->geometry_id), volume_idx(-1) {} model_volume(nullptr), geometry_id(volume->geometry_id), volume_idx(-1) {}
@ -2656,6 +2652,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
else else
m_selection.volumes_changed(map_glvolume_old_to_new); m_selection.volumes_changed(map_glvolume_old_to_new);
// @Enrico suggest this solution to preven accessing pointer on caster without data
m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Volume);
m_gizmos.update_data(); m_gizmos.update_data();
m_gizmos.update_assemble_view_data(); m_gizmos.update_assemble_view_data();
m_gizmos.refresh_on_off_state(); m_gizmos.refresh_on_off_state();
@ -2663,9 +2661,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
// Update the toolbar // Update the toolbar
//BBS: notify the PartPlateList to reload all objects //BBS: notify the PartPlateList to reload all objects
if (update_object_list) if (update_object_list)
{
post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT));
}
//BBS:exclude the assmble view //BBS:exclude the assmble view
if (m_canvas_type != ECanvasType::CanvasAssembleView) { if (m_canvas_type != ECanvasType::CanvasAssembleView) {
@ -2711,14 +2707,19 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
} }
// refresh volume raycasters for picking // refresh volume raycasters for picking
m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Volume);
for (size_t i = 0; i < m_volumes.volumes.size(); ++i) { for (size_t i = 0; i < m_volumes.volumes.size(); ++i) {
assert(m_volumes.volumes[i]->mesh_raycaster != nullptr); const GLVolume* v = m_volumes.volumes[i];
add_raycaster_for_picking(SceneRaycaster::EType::Volume, i, *m_volumes.volumes[i]->mesh_raycaster, m_volumes.volumes[i]->world_matrix()); assert(v->mesh_raycaster != nullptr);
std::shared_ptr<SceneRaycasterItem> raycaster = add_raycaster_for_picking(SceneRaycaster::EType::Volume, i, *v->mesh_raycaster, v->world_matrix());
raycaster->set_active(v->is_active);
} }
// refresh gizmo elements raycasters for picking // refresh gizmo elements raycasters for picking
GLGizmoBase* curr_gizmo = m_gizmos.get_current();
if (curr_gizmo != nullptr)
curr_gizmo->unregister_raycasters_for_picking();
m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Gizmo); m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Gizmo);
m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::FallbackGizmo);
if (curr_gizmo != nullptr && !m_selection.is_empty()) if (curr_gizmo != nullptr && !m_selection.is_empty())
curr_gizmo->register_raycasters_for_picking(); curr_gizmo->register_raycasters_for_picking();
@ -6434,6 +6435,7 @@ void GLCanvas3D::_picking_pass()
break; break;
} }
case SceneRaycaster::EType::Gizmo: case SceneRaycaster::EType::Gizmo:
case SceneRaycaster::EType::FallbackGizmo:
{ {
const Size& cnv_size = get_canvas_size(); const Size& cnv_size = get_canvas_size();
const bool inside = 0 <= m_mouse.position.x() && m_mouse.position.x() < cnv_size.get_width() && const bool inside = 0 <= m_mouse.position.x() && m_mouse.position.x() < cnv_size.get_width() &&
@ -6474,42 +6476,94 @@ void GLCanvas3D::_picking_pass()
{ {
case SceneRaycaster::EType::Bed: { object_type = "Bed"; break; } case SceneRaycaster::EType::Bed: { object_type = "Bed"; break; }
case SceneRaycaster::EType::Gizmo: { object_type = "Gizmo element"; break; } case SceneRaycaster::EType::Gizmo: { object_type = "Gizmo element"; break; }
case SceneRaycaster::EType::FallbackGizmo: { object_type = "Gizmo2 element"; break; }
case SceneRaycaster::EType::Volume: case SceneRaycaster::EType::Volume:
{ {
if (m_volumes.volumes[hit.raycaster_id]->is_wipe_tower) if (m_volumes.volumes[hit.raycaster_id]->is_wipe_tower)
object_type = "Wipe tower"; object_type = "Volume (Wipe tower)";
else if (m_volumes.volumes[hit.raycaster_id]->volume_idx() == -int(slaposPad)) else if (m_volumes.volumes[hit.raycaster_id]->volume_idx() == -int(slaposPad))
object_type = "SLA pad"; object_type = "Volume (SLA pad)";
else if (m_volumes.volumes[hit.raycaster_id]->volume_idx() == -int(slaposSupportTree)) else if (m_volumes.volumes[hit.raycaster_id]->volume_idx() == -int(slaposSupportTree))
object_type = "SLA supports"; object_type = "Volume (SLA supports)";
else if (m_volumes.volumes[hit.raycaster_id]->is_modifier)
object_type = "Volume (Modifier)";
else else
object_type = "Volume"; object_type = "Volume (Part)";
break; break;
} }
default: { break; } default: { break; }
} }
auto add_strings_row_to_table = [&imgui](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color,
const std::string& col_3 = "", const ImVec4& col_3_color = ImGui::GetStyleColorVec4(ImGuiCol_Text)) {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
imgui.text_colored(col_1_color, col_1.c_str());
ImGui::TableSetColumnIndex(1);
imgui.text_colored(col_2_color, col_2.c_str());
if (!col_3.empty()) {
ImGui::TableSetColumnIndex(2);
imgui.text_colored(col_3_color, col_3.c_str());
}
};
char buf[1024]; char buf[1024];
if (hit.type != SceneRaycaster::EType::None) { if (hit.type != SceneRaycaster::EType::None) {
sprintf(buf, "Object ID: %d", hit.raycaster_id); if (ImGui::BeginTable("Hit", 2)) {
imgui.text(std::string(buf)); add_strings_row_to_table("Object ID", ImGuiWrapper::COL_ORANGE_LIGHT, std::to_string(hit.raycaster_id), ImGui::GetStyleColorVec4(ImGuiCol_Text));
sprintf(buf, "Type: %s", object_type.c_str()); add_strings_row_to_table("Type", ImGuiWrapper::COL_ORANGE_LIGHT, object_type, ImGui::GetStyleColorVec4(ImGuiCol_Text));
imgui.text(std::string(buf)); sprintf(buf, "%.3f, %.3f, %.3f", hit.position.x(), hit.position.y(), hit.position.z());
sprintf(buf, "Position: %.3f, %.3f, %.3f", hit.position.x(), hit.position.y(), hit.position.z()); add_strings_row_to_table("Position", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text));
imgui.text(std::string(buf)); sprintf(buf, "%.3f, %.3f, %.3f", hit.normal.x(), hit.normal.y(), hit.normal.z());
sprintf(buf, "Normal: %.3f, %.3f, %.3f", hit.normal.x(), hit.normal.y(), hit.normal.z()); add_strings_row_to_table("Normal", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text));
imgui.text(std::string(buf)); ImGui::EndTable();
}
} }
else else
imgui.text("NO HIT"); imgui.text("NO HIT");
ImGui::Separator(); ImGui::Separator();
imgui.text("Registered for picking:"); imgui.text("Registered for picking:");
sprintf(buf, "Beds: %d", (int)m_scene_raycaster.beds_count()); if (ImGui::BeginTable("Raycasters", 2)) {
imgui.text(std::string(buf)); sprintf(buf, "%d (%d)", (int)m_scene_raycaster.beds_count(), (int)m_scene_raycaster.active_beds_count());
sprintf(buf, "Volumes: %d", (int)m_scene_raycaster.volumes_count()); add_strings_row_to_table("Beds", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text));
imgui.text(std::string(buf)); sprintf(buf, "%d (%d)", (int)m_scene_raycaster.volumes_count(), (int)m_scene_raycaster.active_volumes_count());
sprintf(buf, "Gizmo elements: %d", (int)m_scene_raycaster.gizmos_count()); add_strings_row_to_table("Volumes", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text));
imgui.text(std::string(buf)); sprintf(buf, "%d (%d)", (int)m_scene_raycaster.gizmos_count(), (int)m_scene_raycaster.active_gizmos_count());
add_strings_row_to_table("Gizmo elements", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text));
sprintf(buf, "%d (%d)", (int)m_scene_raycaster.fallback_gizmos_count(), (int)m_scene_raycaster.active_fallback_gizmos_count());
add_strings_row_to_table("Gizmo2 elements", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text));
ImGui::EndTable();
}
std::vector<std::shared_ptr<SceneRaycasterItem>>* gizmo_raycasters = m_scene_raycaster.get_raycasters(SceneRaycaster::EType::Gizmo);
if (gizmo_raycasters != nullptr && !gizmo_raycasters->empty()) {
ImGui::Separator();
imgui.text("Gizmo raycasters IDs:");
if (ImGui::BeginTable("GizmoRaycasters", 3)) {
for (size_t i = 0; i < gizmo_raycasters->size(); ++i) {
add_strings_row_to_table(std::to_string(i), ImGuiWrapper::COL_ORANGE_LIGHT,
std::to_string(SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, (*gizmo_raycasters)[i]->get_id())), ImGui::GetStyleColorVec4(ImGuiCol_Text),
to_string(Geometry::Transformation((*gizmo_raycasters)[i]->get_transform()).get_offset()), ImGui::GetStyleColorVec4(ImGuiCol_Text));
}
ImGui::EndTable();
}
}
std::vector<std::shared_ptr<SceneRaycasterItem>>* gizmo2_raycasters = m_scene_raycaster.get_raycasters(SceneRaycaster::EType::FallbackGizmo);
if (gizmo2_raycasters != nullptr && !gizmo2_raycasters->empty()) {
ImGui::Separator();
imgui.text("Gizmo2 raycasters IDs:");
if (ImGui::BeginTable("Gizmo2Raycasters", 3)) {
for (size_t i = 0; i < gizmo2_raycasters->size(); ++i) {
add_strings_row_to_table(std::to_string(i), ImGuiWrapper::COL_ORANGE_LIGHT,
std::to_string(SceneRaycaster::decode_id(SceneRaycaster::EType::FallbackGizmo, (*gizmo2_raycasters)[i]->get_id())), ImGui::GetStyleColorVec4(ImGuiCol_Text),
to_string(Geometry::Transformation((*gizmo2_raycasters)[i]->get_transform()).get_offset()), ImGui::GetStyleColorVec4(ImGuiCol_Text));
}
ImGui::EndTable();
}
}
imgui.end(); imgui.end();
#endif // ENABLE_RAYCAST_PICKING_DEBUG #endif // ENABLE_RAYCAST_PICKING_DEBUG
} }

View file

@ -1,3 +1,8 @@
///|/ Copyright (c) Prusa Research 2018 - 2023 Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas, David Kocík @kocikdav, Vojtěch Král @vojtechkral
///|/ Copyright (c) BambuStudio 2023 manch1n @manch1n
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLCanvas3D_hpp_ #ifndef slic3r_GLCanvas3D_hpp_
#define slic3r_GLCanvas3D_hpp_ #define slic3r_GLCanvas3D_hpp_
@ -726,7 +731,7 @@ public:
void post_event(wxEvent &&event); void post_event(wxEvent &&event);
std::shared_ptr<SceneRaycasterItem> add_raycaster_for_picking(SceneRaycaster::EType type, int id, const MeshRaycaster& raycaster, std::shared_ptr<SceneRaycasterItem> add_raycaster_for_picking(SceneRaycaster::EType type, int id, const MeshRaycaster& raycaster,
const Transform3d& trafo, bool use_back_faces = false) { const Transform3d& trafo = Transform3d::Identity(), bool use_back_faces = false) {
return m_scene_raycaster.add_raycaster(type, id, raycaster, trafo, use_back_faces); return m_scene_raycaster.add_raycaster(type, id, raycaster, trafo, use_back_faces);
} }
void remove_raycasters_for_picking(SceneRaycaster::EType type, int id) { void remove_raycasters_for_picking(SceneRaycaster::EType type, int id) {

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2020 - 2023 Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Filip Sykala @Jony01
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "libslic3r/libslic3r.h" #include "libslic3r/libslic3r.h"
#include "GLModel.hpp" #include "GLModel.hpp"
@ -247,6 +251,21 @@ void GLModel::Geometry::remove_vertex(size_t id)
} }
} }
indexed_triangle_set GLModel::Geometry::get_as_indexed_triangle_set() const
{
indexed_triangle_set its;
its.vertices.reserve(vertices_count());
for (size_t i = 0; i < vertices_count(); ++i) {
its.vertices.emplace_back(extract_position_3(i));
}
its.indices.reserve(indices_count() / 3);
for (size_t i = 0; i < indices_count() / 3; ++i) {
const size_t tri_id = i * 3;
its.indices.emplace_back(extract_index(tri_id), extract_index(tri_id + 1), extract_index(tri_id + 2));
}
return its;
}
size_t GLModel::Geometry::vertex_stride_floats(const Format& format) size_t GLModel::Geometry::vertex_stride_floats(const Format& format)
{ {
switch (format.vertex_layout) switch (format.vertex_layout)
@ -1212,5 +1231,190 @@ GLModel::Geometry diamond(unsigned int resolution)
return data; return data;
} }
GLModel::Geometry smooth_sphere(unsigned int resolution, float radius)
{
resolution = std::max<unsigned int>(4, resolution);
const unsigned int sectorCount = resolution;
const unsigned int stackCount = resolution;
const float sectorStep = float(2.0 * M_PI / sectorCount);
const float stackStep = float(M_PI / stackCount);
GLModel::Geometry data;
data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
data.reserve_vertices((stackCount - 1) * sectorCount + 2);
data.reserve_indices((2 * (stackCount - 1) * sectorCount) * 3);
// vertices
for (unsigned int i = 0; i <= stackCount; ++i) {
// from pi/2 to -pi/2
const double stackAngle = 0.5 * M_PI - stackStep * i;
const double xy = double(radius) * ::cos(stackAngle);
const double z = double(radius) * ::sin(stackAngle);
if (i == 0 || i == stackCount) {
const Vec3f v(float(xy), 0.0f, float(z));
data.add_vertex(v, (Vec3f)v.normalized());
}
else {
for (unsigned int j = 0; j < sectorCount; ++j) {
// from 0 to 2pi
const double sectorAngle = sectorStep * j;
const Vec3f v(float(xy * std::cos(sectorAngle)), float(xy * std::sin(sectorAngle)), float(z));
data.add_vertex(v, (Vec3f)v.normalized());
}
}
}
// triangles
for (unsigned int i = 0; i < stackCount; ++i) {
// Beginning of current stack.
unsigned int k1 = (i == 0) ? 0 : (1 + (i - 1) * sectorCount);
const unsigned int k1_first = k1;
// Beginning of next stack.
unsigned int k2 = (i == 0) ? 1 : (k1 + sectorCount);
const unsigned int k2_first = k2;
for (unsigned int j = 0; j < sectorCount; ++j) {
// 2 triangles per sector excluding first and last stacks
unsigned int k1_next = k1;
unsigned int k2_next = k2;
if (i != 0) {
k1_next = (j + 1 == sectorCount) ? k1_first : (k1 + 1);
data.add_triangle(k1, k2, k1_next);
}
if (i + 1 != stackCount) {
k2_next = (j + 1 == sectorCount) ? k2_first : (k2 + 1);
data.add_triangle(k1_next, k2, k2_next);
}
k1 = k1_next;
k2 = k2_next;
}
}
return data;
}
GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float height)
{
resolution = std::max<unsigned int>(4, resolution);
const unsigned int sectorCount = resolution;
const float sectorStep = 2.0f * float(M_PI) / float(sectorCount);
GLModel::Geometry data;
data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
data.reserve_vertices(sectorCount * 4 + 2);
data.reserve_indices(sectorCount * 4 * 3);
auto generate_vertices_on_circle = [sectorCount, sectorStep](float radius) {
std::vector<Vec3f> ret;
ret.reserve(sectorCount);
for (unsigned int i = 0; i < sectorCount; ++i) {
// from 0 to 2pi
const float sectorAngle = sectorStep * i;
ret.emplace_back(radius * std::cos(sectorAngle), radius * std::sin(sectorAngle), 0.0f);
}
return ret;
};
const std::vector<Vec3f> base_vertices = generate_vertices_on_circle(radius);
const Vec3f h = height * Vec3f::UnitZ();
// stem vertices
for (unsigned int i = 0; i < sectorCount; ++i) {
const Vec3f& v = base_vertices[i];
const Vec3f n = v.normalized();
data.add_vertex(v, n);
data.add_vertex(v + h, n);
}
// stem triangles
for (unsigned int i = 0; i < sectorCount; ++i) {
unsigned int v1 = i * 2;
unsigned int v2 = (i < sectorCount - 1) ? v1 + 2 : 0;
unsigned int v3 = v2 + 1;
unsigned int v4 = v1 + 1;
data.add_triangle(v1, v2, v3);
data.add_triangle(v1, v3, v4);
}
// bottom cap vertices
Vec3f cap_center = Vec3f::Zero();
unsigned int cap_center_id = data.vertices_count();
Vec3f normal = -Vec3f::UnitZ();
data.add_vertex(cap_center, normal);
for (unsigned int i = 0; i < sectorCount; ++i) {
data.add_vertex(base_vertices[i], normal);
}
// bottom cap triangles
for (unsigned int i = 0; i < sectorCount; ++i) {
data.add_triangle(cap_center_id, (i < sectorCount - 1) ? cap_center_id + i + 2 : cap_center_id + 1, cap_center_id + i + 1);
}
// top cap vertices
cap_center += h;
cap_center_id = data.vertices_count();
normal = -normal;
data.add_vertex(cap_center, normal);
for (unsigned int i = 0; i < sectorCount; ++i) {
data.add_vertex(base_vertices[i] + h, normal);
}
// top cap triangles
for (unsigned int i = 0; i < sectorCount; ++i) {
data.add_triangle(cap_center_id, cap_center_id + i + 1, (i < sectorCount - 1) ? cap_center_id + i + 2 : cap_center_id + 1);
}
return data;
}
GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int secondary_resolution, float radius, float thickness)
{
const unsigned int torus_sector_count = std::max<unsigned int>(4, primary_resolution);
const float torus_sector_step = 2.0f * float(M_PI) / float(torus_sector_count);
const unsigned int section_sector_count = std::max<unsigned int>(4, secondary_resolution);
const float section_sector_step = 2.0f * float(M_PI) / float(section_sector_count);
GLModel::Geometry data;
data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
data.reserve_vertices(torus_sector_count * section_sector_count);
data.reserve_indices(torus_sector_count * section_sector_count * 2 * 3);
// vertices
for (unsigned int i = 0; i < torus_sector_count; ++i) {
const float section_angle = torus_sector_step * i;
const float csa = std::cos(section_angle);
const float ssa = std::sin(section_angle);
const Vec3f section_center(radius * csa, radius * ssa, 0.0f);
for (unsigned int j = 0; j < section_sector_count; ++j) {
const float circle_angle = section_sector_step * j;
const float thickness_xy = thickness * std::cos(circle_angle);
const float thickness_z = thickness * std::sin(circle_angle);
const Vec3f v(thickness_xy * csa, thickness_xy * ssa, thickness_z);
data.add_vertex(section_center + v, (Vec3f)v.normalized());
}
}
// triangles
for (unsigned int i = 0; i < torus_sector_count; ++i) {
const unsigned int ii = i * section_sector_count;
const unsigned int ii_next = ((i + 1) % torus_sector_count) * section_sector_count;
for (unsigned int j = 0; j < section_sector_count; ++j) {
const unsigned int j_next = (j + 1) % section_sector_count;
const unsigned int i0 = ii + j;
const unsigned int i1 = ii_next + j;
const unsigned int i2 = ii_next + j_next;
const unsigned int i3 = ii + j_next;
data.add_triangle(i0, i1, i2);
data.add_triangle(i0, i2, i3);
}
}
return data;
}
} // namespace GUI } // namespace GUI
} // namespace Slic3r } // namespace Slic3r

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2020 - 2023 Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Filip Sykala @Jony01, Lukáš Matěna @lukasmatena
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLModel_hpp_ #ifndef slic3r_GLModel_hpp_
#define slic3r_GLModel_hpp_ #define slic3r_GLModel_hpp_
@ -101,6 +105,8 @@ namespace GUI {
size_t vertices_size_bytes() const { return vertices_size_floats() * sizeof(float); } size_t vertices_size_bytes() const { return vertices_size_floats() * sizeof(float); }
size_t indices_size_bytes() const { return indices.size() * index_stride_bytes(*this); } size_t indices_size_bytes() const { return indices.size() * index_stride_bytes(*this); }
indexed_triangle_set get_as_indexed_triangle_set() const;
static size_t vertex_stride_floats(const Format& format); static size_t vertex_stride_floats(const Format& format);
static size_t vertex_stride_bytes(const Format& format) { return vertex_stride_floats(format) * sizeof(float); } static size_t vertex_stride_bytes(const Format& format) { return vertex_stride_floats(format) * sizeof(float); }
@ -233,6 +239,18 @@ namespace GUI {
// the diamond is contained into a box with size [1, 1, 1] // the diamond is contained into a box with size [1, 1, 1]
GLModel::Geometry diamond(unsigned int resolution); GLModel::Geometry diamond(unsigned int resolution);
// create a sphere with smooth normals
// the origin of the sphere is in its center
GLModel::Geometry smooth_sphere(unsigned int resolution, float radius);
// create a cylinder with smooth normals
// the axis of the cylinder is the Z axis
// the origin of the cylinder is the center of its bottom cap face
GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float height);
// create a torus with smooth normals
// the axis of the torus is the Z axis
// the origin of the torus is in its center
GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int secondary_resolution, float radius, float thickness);
} // namespace GUI } // namespace GUI
} // namespace Slic3r } // namespace Slic3r

View file

@ -0,0 +1,13 @@
///|/ Copyright (c) Prusa Research 2021 Enrico Turri @enricoturri1966
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "libslic3r/libslic3r.h"
#include "GUI_Geometry.hpp"
namespace Slic3r {
namespace GUI {
} // namespace Slic3r
} // namespace GUI

View file

@ -0,0 +1,82 @@
///|/ Copyright (c) Prusa Research 2021 - 2023 Enrico Turri @enricoturri1966
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GUI_Geometry_hpp_
#define slic3r_GUI_Geometry_hpp_
namespace Slic3r {
namespace GUI {
enum class ECoordinatesType : unsigned char
{
World,
Instance,
Local
};
class TransformationType
{
public:
enum Enum {
// Transforming in a world coordinate system
World = 0,
// Transforming in a instance coordinate system
Instance = 1,
// Transforming in a local coordinate system
Local = 2,
// Absolute transformations, allowed in local coordinate system only.
Absolute = 0,
// Relative transformations, allowed in both local and world coordinate system.
Relative = 4,
// For group selection, the transformation is performed as if the group made a single solid body.
Joint = 0,
// For group selection, the transformation is performed on each object independently.
Independent = 8,
World_Relative_Joint = World | Relative | Joint,
World_Relative_Independent = World | Relative | Independent,
Instance_Absolute_Joint = Instance | Absolute | Joint,
Instance_Absolute_Independent = Instance | Absolute | Independent,
Instance_Relative_Joint = Instance | Relative | Joint,
Instance_Relative_Independent = Instance | Relative | Independent,
Local_Absolute_Joint = Local | Absolute | Joint,
Local_Absolute_Independent = Local | Absolute | Independent,
Local_Relative_Joint = Local | Relative | Joint,
Local_Relative_Independent = Local | Relative | Independent,
};
TransformationType() : m_value(World) {}
TransformationType(Enum value) : m_value(value) {}
TransformationType& operator=(Enum value) { m_value = value; return *this; }
Enum operator()() const { return m_value; }
bool has(Enum v) const { return ((unsigned int)m_value & (unsigned int)v) != 0; }
void set_world() { this->remove(Instance); this->remove(Local); }
void set_instance() { this->remove(Local); this->add(Instance); }
void set_local() { this->remove(Instance); this->add(Local); }
void set_absolute() { this->remove(Relative); }
void set_relative() { this->add(Relative); }
void set_joint() { this->remove(Independent); }
void set_independent() { this->add(Independent); }
bool world() const { return !this->has(Instance) && !this->has(Local); }
bool instance() const { return this->has(Instance); }
bool local() const { return this->has(Local); }
bool absolute() const { return !this->has(Relative); }
bool relative() const { return this->has(Relative); }
bool joint() const { return !this->has(Independent); }
bool independent() const { return this->has(Independent); }
private:
void add(Enum v) { m_value = Enum((unsigned int)m_value | (unsigned int)v); }
void remove(Enum v) { m_value = Enum((unsigned int)m_value & (~(unsigned int)v)); }
Enum m_value;
};
} // namespace Slic3r
} // namespace GUI
#endif // slic3r_GUI_Geometry_hpp_

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GUI_Utils_hpp_ #ifndef slic3r_GUI_Utils_hpp_
#define slic3r_GUI_Utils_hpp_ #define slic3r_GUI_Utils_hpp_
@ -463,6 +467,16 @@ public:
~TaskTimer(); ~TaskTimer();
}; };
class KeyAutoRepeatFilter
{
size_t m_count{ 0 };
public:
void increase_count() { ++m_count; }
void reset_count() { m_count = 0; }
bool is_first() const { return m_count == 0; }
};
/* Image Generator */ /* Image Generator */
#define _3MF_COVER_SIZE wxSize(240, 240) #define _3MF_COVER_SIZE wxSize(240, 240)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,190 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Filip Sykala @Jony01
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLGizmoMeasure_hpp_
#define slic3r_GLGizmoMeasure_hpp_
#include "GLGizmoBase.hpp"
#include "slic3r/GUI/GLModel.hpp"
#include "slic3r/GUI/GUI_Utils.hpp"
#include "slic3r/GUI/MeshUtils.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "libslic3r/Measure.hpp"
#include "libslic3r/Model.hpp"
namespace Slic3r {
enum class ModelVolumeType : int;
namespace Measure { class Measuring; }
namespace GUI {
enum class SLAGizmoEventType : unsigned char;
class GLGizmoMeasure : public GLGizmoBase
{
enum class EMode : unsigned char
{
FeatureSelection,
PointSelection
};
struct SelectedFeatures
{
struct Item
{
bool is_center{ false };
std::optional<Measure::SurfaceFeature> source;
std::optional<Measure::SurfaceFeature> feature;
bool operator == (const Item& other) const {
return this->is_center == other.is_center && this->source == other.source && this->feature == other.feature;
}
bool operator != (const Item& other) const {
return !operator == (other);
}
void reset() {
is_center = false;
source.reset();
feature.reset();
}
};
Item first;
Item second;
void reset() {
first.reset();
second.reset();
}
bool operator == (const SelectedFeatures & other) const {
if (this->first != other.first) return false;
return this->second == other.second;
}
bool operator != (const SelectedFeatures & other) const {
return !operator == (other);
}
};
struct VolumeCacheItem
{
const ModelObject* object{ nullptr };
const ModelInstance* instance{ nullptr };
const ModelVolume* volume{ nullptr };
Transform3d world_trafo;
bool operator == (const VolumeCacheItem& other) const {
return this->object == other.object && this->instance == other.instance && this->volume == other.volume &&
this->world_trafo.isApprox(other.world_trafo);
}
};
std::vector<VolumeCacheItem> m_volumes_cache;
EMode m_mode{ EMode::FeatureSelection };
Measure::MeasurementResult m_measurement_result;
std::unique_ptr<Measure::Measuring> m_measuring; // PIMPL
PickingModel m_sphere;
PickingModel m_cylinder;
PickingModel m_circle;
PickingModel m_plane;
struct Dimensioning
{
GLModel line;
GLModel triangle;
GLModel arc;
};
Dimensioning m_dimensioning;
// Uses a standalone raycaster and not the shared one because of the
// difference in how the mesh is updated
std::unique_ptr<MeshRaycaster> m_raycaster;
std::vector<GLModel> m_plane_models_cache;
std::map<int, std::shared_ptr<SceneRaycasterItem>> m_raycasters;
// used to keep the raycasters for point/center spheres
std::vector<std::shared_ptr<SceneRaycasterItem>> m_selected_sphere_raycasters;
std::optional<Measure::SurfaceFeature> m_curr_feature;
std::optional<Vec3d> m_curr_point_on_feature_position;
struct SceneRaycasterState
{
std::shared_ptr<SceneRaycasterItem> raycaster{ nullptr };
bool state{true};
};
std::vector<SceneRaycasterState> m_scene_raycasters;
// These hold information to decide whether recalculation is necessary:
float m_last_inv_zoom{ 0.0f };
std::optional<Measure::SurfaceFeature> m_last_circle;
int m_last_plane_idx{ -1 };
bool m_mouse_left_down{ false }; // for detection left_up of this gizmo
Vec2d m_mouse_pos{ Vec2d::Zero() };
KeyAutoRepeatFilter m_shift_kar_filter;
SelectedFeatures m_selected_features;
bool m_pending_scale{ false };
bool m_editing_distance{ false };
bool m_is_editing_distance_first_frame{ true };
void update_if_needed();
void disable_scene_raycasters();
void restore_scene_raycasters_state();
void render_dimensioning();
#if ENABLE_MEASURE_GIZMO_DEBUG
void render_debug_dialog();
#endif // ENABLE_MEASURE_GIZMO_DEBUG
public:
GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
/// <summary>
/// Apply rotation on select plane
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>Return True when use the information otherwise False.</returns>
bool on_mouse(const wxMouseEvent &mouse_event) override;
void data_changed(bool is_serializing) override;
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
bool wants_enter_leave_snapshots() const override { return true; }
std::string get_gizmo_entering_text() const override { return _u8L("Entering Measure gizmo"); }
std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Measure gizmo"); }
std::string get_action_snapshot_name() const override { return _u8L("Measure gizmo editing"); }
protected:
bool on_init() override;
std::string on_get_name() const override;
bool on_is_activable() const override;
void on_render() override;
void on_set_state() override;
virtual void on_render_input_window(float x, float y, float bottom_limit) override;
virtual void on_register_raycasters_for_picking() override;
virtual void on_unregister_raycasters_for_picking() override;
void remove_selected_sphere_raycaster(int id);
void update_measurement_result();
};
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GLGizmoMeasure_hpp_

View file

@ -30,8 +30,12 @@ enum class SLAGizmoEventType : unsigned char {
Dragging, Dragging,
Delete, Delete,
SelectAll, SelectAll,
CtrlDown,
CtrlUp,
ShiftDown,
ShiftUp, ShiftUp,
AltUp, AltUp,
Escape,
ApplyChanges, ApplyChanges,
DiscardChanges, DiscardChanges,
AutomaticGeneration, AutomaticGeneration,

View file

@ -27,6 +27,7 @@
#include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp" #include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoMeasure.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoText.hpp" #include "slic3r/GUI/Gizmos/GLGizmoText.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp" #include "slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp"
@ -195,6 +196,7 @@ bool GLGizmosManager::init()
m_gizmos.emplace_back(new GLGizmoSeam(m_parent, m_is_dark ? "toolbar_seam_dark.svg" : "toolbar_seam.svg", EType::Seam)); m_gizmos.emplace_back(new GLGizmoSeam(m_parent, m_is_dark ? "toolbar_seam_dark.svg" : "toolbar_seam.svg", EType::Seam));
m_gizmos.emplace_back(new GLGizmoText(m_parent, m_is_dark ? "toolbar_text_dark.svg" : "toolbar_text.svg", EType::Text)); m_gizmos.emplace_back(new GLGizmoText(m_parent, m_is_dark ? "toolbar_text_dark.svg" : "toolbar_text.svg", EType::Text));
m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, m_is_dark ? "mmu_segmentation_dark.svg" : "mmu_segmentation.svg", EType::MmuSegmentation)); m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, m_is_dark ? "mmu_segmentation_dark.svg" : "mmu_segmentation.svg", EType::MmuSegmentation));
m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, "measure.svg", EType::Measure));
m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "reduce_triangles.svg", EType::Simplify)); m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "reduce_triangles.svg", EType::Simplify));
//m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", sprite_id++)); //m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", sprite_id++));
//m_gizmos.emplace_back(new GLGizmoFaceDetector(m_parent, "face recognition.svg", sprite_id++)); //m_gizmos.emplace_back(new GLGizmoFaceDetector(m_parent, "face recognition.svg", sprite_id++));
@ -438,6 +440,8 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p
return dynamic_cast<GLGizmoMmuSegmentation*>(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); return dynamic_cast<GLGizmoMmuSegmentation*>(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == Text) else if (m_current == Text)
return dynamic_cast<GLGizmoText*>(m_gizmos[Text].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); return dynamic_cast<GLGizmoText*>(m_gizmos[Text].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == Measure)
return dynamic_cast<GLGizmoMeasure*>(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == Cut) else if (m_current == Cut)
return dynamic_cast<GLGizmoAdvancedCut *>(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); return dynamic_cast<GLGizmoAdvancedCut *>(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == MeshBoolean) else if (m_current == MeshBoolean)
@ -689,8 +693,10 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
// key ESC // key ESC
case WXK_ESCAPE: case WXK_ESCAPE:
{ {
if (m_current != Undefined) if (m_current != Undefined) {
{ if (m_current == Measure && gizmo_event(SLAGizmoEventType::Escape)) {
// do nothing
} else
//if ((m_current != SlaSupports) || !gizmo_event(SLAGizmoEventType::DiscardChanges)) //if ((m_current != SlaSupports) || !gizmo_event(SLAGizmoEventType::DiscardChanges))
reset_all_states(); reset_all_states();
@ -698,7 +704,14 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
} }
break; break;
} }
//skip some keys when gizmo case WXK_BACK:
case WXK_DELETE:
{
if ((m_current == Cut || m_current == Measure) && gizmo_event(SLAGizmoEventType::Delete))
processed = true;
break;
}
case 'A': case 'A':
case 'a': case 'a':
{ {
@ -827,6 +840,12 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
processed = true; processed = true;
} }
}*/ }*/
if (m_current == Measure) {
if (keyCode == WXK_CONTROL)
gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
else if (keyCode == WXK_SHIFT)
gizmo_event(SLAGizmoEventType::ShiftUp, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
}
// if (processed) // if (processed)
// m_parent.set_cursor(GLCanvas3D::Standard); // m_parent.set_cursor(GLCanvas3D::Standard);
@ -897,6 +916,12 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
wxGetApp().imgui()->set_requires_extra_frame(); wxGetApp().imgui()->set_requires_extra_frame();
} }
} }
else if (m_current == Measure) {
if (keyCode == WXK_CONTROL)
gizmo_event(SLAGizmoEventType::CtrlDown, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
else if (keyCode == WXK_SHIFT)
gizmo_event(SLAGizmoEventType::ShiftDown, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
}
} }
if (processed) if (processed)

View file

@ -88,6 +88,7 @@ public:
// BBS // BBS
Text, Text,
MmuSegmentation, MmuSegmentation,
Measure,
Simplify, Simplify,
//SlaSupports, //SlaSupports,
// BBS // BBS

View file

@ -1,3 +1,8 @@
///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, David Kocík @kocikdav, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Filip Sykala @Jony01, Lukáš Hejl @hejllukas, Vojtěch Král @vojtechkral
///|/ Copyright (c) 2019 Jason Tibbitts @jasontibbitts
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "ImGuiWrapper.hpp" #include "ImGuiWrapper.hpp"
#include <cstdio> #include <cstdio>
@ -63,6 +68,7 @@ static const std::map<const wchar_t, std::string> font_icons = {
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT #if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
{ImGui::SliderFloatEditBtnIcon, "edit_button" }, {ImGui::SliderFloatEditBtnIcon, "edit_button" },
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT #endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
{ImGui::ClipboardBtnIcon , "copy_menu" },
{ImGui::CircleButtonIcon , "circle_paint" }, {ImGui::CircleButtonIcon , "circle_paint" },
{ImGui::TriangleButtonIcon , "triangle_paint" }, {ImGui::TriangleButtonIcon , "triangle_paint" },
{ImGui::FillButtonIcon , "fill_paint" }, {ImGui::FillButtonIcon , "fill_paint" },
@ -789,11 +795,6 @@ bool ImGuiWrapper::radio_button(const wxString &label, bool active)
return ImGui::RadioButton(label_utf8.c_str(), active); return ImGui::RadioButton(label_utf8.c_str(), active);
} }
bool ImGuiWrapper::image_button()
{
return false;
}
bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format) bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format)
{ {
return ImGui::InputDouble(label.c_str(), const_cast<double*>(&value), 0.0f, 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal); return ImGui::InputDouble(label.c_str(), const_cast<double*>(&value), 0.0f, 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal);
@ -1035,6 +1036,71 @@ bool ImGuiWrapper::slider_float(const wxString& label, float* v, float v_min, fl
} }
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT #endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
static bool image_button_ex(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& padding, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return false;
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2);
ImGui::ItemSize(bb);
if (!ImGui::ItemAdd(bb, id))
return false;
bool hovered, held;
bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, flags);
// Render
const ImU32 col = ImGui::GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
ImGui::RenderNavHighlight(bb, id);
ImGui::RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, g.Style.FrameRounding));
if (bg_col.w > 0.0f)
window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, ImGui::GetColorU32(bg_col));
window->DrawList->AddImage(texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, ImGui::GetColorU32(tint_col));
return pressed;
}
bool ImGuiWrapper::image_button(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (window->SkipItems)
return false;
// Default to using texture ID as ID. User can still push string/integer prefixes.
ImGui::PushID((void*)(intptr_t)user_texture_id);
const ImGuiID id = window->GetID("#image");
ImGui::PopID();
const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : g.Style.FramePadding;
return image_button_ex(id, user_texture_id, size, uv0, uv1, padding, bg_col, tint_col, flags);
}
bool ImGuiWrapper::image_button(const wchar_t icon, const wxString& tooltip)
{
const ImGuiIO& io = ImGui::GetIO();
const ImTextureID tex_id = io.Fonts->TexID;
assert(io.Fonts->TexWidth > 0 && io.Fonts->TexHeight > 0);
const float inv_tex_w = 1.0f / float(io.Fonts->TexWidth);
const float inv_tex_h = 1.0f / float(io.Fonts->TexHeight);
const ImFontAtlasCustomRect* const rect = GetTextureCustomRect(icon);
const ImVec2 size = { float(rect->Width), float(rect->Height) };
const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h);
const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h);
ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f });
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, 1.0f });
ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.25f, 0.25f, 0.25f, 1.0f });
const bool res = image_button(tex_id, size, uv0, uv1);
ImGui::PopStyleColor(3);
if (!tooltip.empty() && ImGui::IsItemHovered())
this->tooltip(tooltip, ImGui::GetFontSize() * 20.0f);
return res;
}
bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>& options, int& selection) bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>& options, int& selection)
{ {
// this is to force the label to the left of the widget: // this is to force the label to the left of the widget:
@ -1780,6 +1846,17 @@ bool ImGuiWrapper::want_any_input() const
return io.WantCaptureMouse || io.WantCaptureKeyboard || io.WantTextInput; return io.WantCaptureMouse || io.WantCaptureKeyboard || io.WantTextInput;
} }
ImFontAtlasCustomRect* ImGuiWrapper::GetTextureCustomRect(const wchar_t& tex_id)
{
auto item = m_custom_glyph_rects_ids.find(tex_id);
return (item != m_custom_glyph_rects_ids.end()) ? ImGui::GetIO().Fonts->GetCustomRectByIndex(m_custom_glyph_rects_ids[tex_id]) : nullptr;
}
void ImGuiWrapper::disable_background_fadeout_animation()
{
GImGui->DimBgRatio = 1.0f;
}
ImU32 ImGuiWrapper::to_ImU32(const ColorRGBA& color) ImU32 ImGuiWrapper::to_ImU32(const ColorRGBA& color)
{ {
return ImGui::GetColorU32({ color.r(), color.g(), color.b(), color.a() }); return ImGui::GetColorU32({ color.r(), color.g(), color.b(), color.a() });
@ -2151,12 +2228,18 @@ void ImGuiWrapper::init_font(bool compress)
int rect_id = io.Fonts->CustomRects.Size; // id of the rectangle added next int rect_id = io.Fonts->CustomRects.Size; // id of the rectangle added next
// add rectangles for the icons to the font atlas // add rectangles for the icons to the font atlas
for (auto& icon : font_icons) for (auto& icon : font_icons) {
m_custom_glyph_rects_ids[icon.first] =
io.Fonts->AddCustomRectFontGlyph(default_font, icon.first, icon_sz, icon_sz, 3.0 * font_scale + icon_sz); io.Fonts->AddCustomRectFontGlyph(default_font, icon.first, icon_sz, icon_sz, 3.0 * font_scale + icon_sz);
for (auto& icon : font_icons_large) }
for (auto& icon : font_icons_large) {
m_custom_glyph_rects_ids[icon.first] =
io.Fonts->AddCustomRectFontGlyph(default_font, icon.first, icon_sz * 2, icon_sz * 2, 3.0 * font_scale + icon_sz * 2); io.Fonts->AddCustomRectFontGlyph(default_font, icon.first, icon_sz * 2, icon_sz * 2, 3.0 * font_scale + icon_sz * 2);
for (auto& icon : font_icons_extra_large) }
for (auto& icon : font_icons_extra_large) {
m_custom_glyph_rects_ids[icon.first] =
io.Fonts->AddCustomRectFontGlyph(default_font, icon.first, icon_sz * 4, icon_sz * 4, 3.0 * font_scale + icon_sz * 4); io.Fonts->AddCustomRectFontGlyph(default_font, icon.first, icon_sz * 4, icon_sz * 4, 3.0 * font_scale + icon_sz * 4);
}
// Build texture atlas // Build texture atlas
unsigned char* pixels; unsigned char* pixels;
@ -2164,13 +2247,13 @@ void ImGuiWrapper::init_font(bool compress)
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
BOOST_LOG_TRIVIAL(trace) << "Build default font texture done. width: " << width << ", height: " << height; BOOST_LOG_TRIVIAL(trace) << "Build default font texture done. width: " << width << ", height: " << height;
// Fill rectangles from the SVG-icons auto load_icon_from_svg = [this, &io, pixels, width, &rect_id](const std::pair<const wchar_t, std::string> icon, int icon_sz) {
for (auto icon : font_icons) {
if (const ImFontAtlas::CustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id)) { if (const ImFontAtlas::CustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id)) {
assert(rect->Width == icon_sz); assert(rect->Width == icon_sz);
assert(rect->Height == icon_sz); assert(rect->Height == icon_sz);
unsigned outwidth, outheight; unsigned outwidth, outheight;
std::vector<unsigned char> raw_data = load_svg(icon.second, icon_sz, icon_sz, &outwidth, &outheight); std::vector<unsigned char> raw_data = load_svg(icon.second, icon_sz, icon_sz, &outwidth, &outheight);
if (!raw_data.empty()) {
const ImU32* pIn = (ImU32*)raw_data.data(); const ImU32* pIn = (ImU32*)raw_data.data();
for (unsigned y = 0; y < outheight; y++) { for (unsigned y = 0; y < outheight; y++) {
ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X); ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X);
@ -2178,41 +2261,23 @@ void ImGuiWrapper::init_font(bool compress)
*pOut++ = *pIn++; *pOut++ = *pIn++;
} }
} }
}
rect_id++; rect_id++;
};
// Fill rectangles from the SVG-icons
for (auto icon : font_icons) {
load_icon_from_svg(icon, icon_sz);
} }
icon_sz *= 2; // default size of large icon is 32 px icon_sz *= 2; // default size of large icon is 32 px
for (auto icon : font_icons_large) { for (auto icon : font_icons_large) {
if (const ImFontAtlas::CustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id)) { load_icon_from_svg(icon, icon_sz);
assert(rect->Width == icon_sz);
assert(rect->Height == icon_sz);
unsigned outwidth, outheight;
std::vector<unsigned char> raw_data = load_svg(icon.second, icon_sz, icon_sz, &outwidth, &outheight);
const ImU32* pIn = (ImU32*)raw_data.data();
for (unsigned y = 0; y < outheight; y++) {
ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X);
for (unsigned x = 0; x < outwidth; x++)
*pOut++ = *pIn++;
}
}
rect_id++;
} }
icon_sz *= 2; // default size of extra large icon is 64 px icon_sz *= 2; // default size of extra large icon is 64 px
for (auto icon : font_icons_extra_large) { for (auto icon : font_icons_extra_large) {
if (const ImFontAtlas::CustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id)) { load_icon_from_svg(icon, icon_sz);
assert(rect->Width == icon_sz);
assert(rect->Height == icon_sz);
unsigned outwidth, outheight;
std::vector<unsigned char> raw_data = load_svg(icon.second, icon_sz, icon_sz, &outwidth, &outheight);
const ImU32* pIn = (ImU32*)raw_data.data();
for (unsigned y = 0; y < outheight; y++) {
ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X);
for (unsigned x = 0; x < outwidth; x++)
*pOut++ = *pIn++;
}
}
rect_id++;
} }
// Upload texture to graphics system // Upload texture to graphics system

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas, David Kocík @kocikdav, Vojtěch Král @vojtechkral
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_ImGuiWrapper_hpp_ #ifndef slic3r_ImGuiWrapper_hpp_
#define slic3r_ImGuiWrapper_hpp_ #define slic3r_ImGuiWrapper_hpp_
@ -61,6 +65,7 @@ class ImGuiWrapper
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT #if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
bool m_requires_extra_frame{ false }; bool m_requires_extra_frame{ false };
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT #endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
std::map<wchar_t, int> m_custom_glyph_rects_ids;
std::string m_clipboard_text; std::string m_clipboard_text;
public: public:
@ -117,7 +122,6 @@ public:
bool bbl_button(const wxString &label); bool bbl_button(const wxString &label);
bool button(const wxString& label, float width, float height); bool button(const wxString& label, float width, float height);
bool radio_button(const wxString &label, bool active); bool radio_button(const wxString &label, bool active);
bool image_button();
bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f"); bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f");
bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f"); bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f");
bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f"); bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f");
@ -150,6 +154,9 @@ public:
bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true); bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true);
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT #endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
bool image_button(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0.0, 0.0), const ImVec2& uv1 = ImVec2(1.0, 1.0), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0.0, 0.0, 0.0, 0.0), const ImVec4& tint_col = ImVec4(1.0, 1.0, 1.0, 1.0), ImGuiButtonFlags flags = 0);
bool image_button(const wchar_t icon, const wxString& tooltip = L"");
bool combo(const wxString& label, const std::vector<std::string>& options, int& selection); // Use -1 to not mark any option as selected bool combo(const wxString& label, const std::vector<std::string>& options, int& selection); // Use -1 to not mark any option as selected
bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected, int& mouse_wheel); bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected, int& mouse_wheel);
void search_list(const ImVec2& size, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str, void search_list(const ImVec2& size, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str,
@ -184,11 +191,15 @@ public:
void reset_requires_extra_frame() { m_requires_extra_frame = false; } void reset_requires_extra_frame() { m_requires_extra_frame = false; }
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT #endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
void disable_background_fadeout_animation();
static ImU32 to_ImU32(const ColorRGBA& color); static ImU32 to_ImU32(const ColorRGBA& color);
static ImVec4 to_ImVec4(const ColorRGBA& color); static ImVec4 to_ImVec4(const ColorRGBA& color);
static ColorRGBA from_ImU32(const ImU32& color); static ColorRGBA from_ImU32(const ImU32& color);
static ColorRGBA from_ImVec4(const ImVec4& color); static ColorRGBA from_ImVec4(const ImVec4& color);
ImFontAtlasCustomRect* GetTextureCustomRect(const wchar_t& tex_id);
static const ImVec4 COL_GREY_DARK; static const ImVec4 COL_GREY_DARK;
static const ImVec4 COL_GREY_LIGHT; static const ImVec4 COL_GREY_LIGHT;
static const ImVec4 COL_ORANGE_DARK; static const ImVec4 COL_ORANGE_DARK;

View file

@ -1,8 +1,14 @@
///|/ Copyright (c) Prusa Research 2022 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "libslic3r/libslic3r.h" #include "libslic3r/libslic3r.h"
#include "SceneRaycaster.hpp" #include "SceneRaycaster.hpp"
#include "Camera.hpp" #include "Camera.hpp"
#include "GUI_App.hpp" #include "GUI_App.hpp"
#include "Selection.hpp"
#include "Plater.hpp"
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {
@ -38,6 +44,7 @@ std::shared_ptr<SceneRaycasterItem> SceneRaycaster::add_raycaster(EType type, in
case EType::Bed: { return m_bed.emplace_back(std::make_shared<SceneRaycasterItem>(encode_id(type, id), raycaster, trafo, use_back_faces)); } case EType::Bed: { return m_bed.emplace_back(std::make_shared<SceneRaycasterItem>(encode_id(type, id), raycaster, trafo, use_back_faces)); }
case EType::Volume: { return m_volumes.emplace_back(std::make_shared<SceneRaycasterItem>(encode_id(type, id), raycaster, trafo, use_back_faces)); } case EType::Volume: { return m_volumes.emplace_back(std::make_shared<SceneRaycasterItem>(encode_id(type, id), raycaster, trafo, use_back_faces)); }
case EType::Gizmo: { return m_gizmos.emplace_back(std::make_shared<SceneRaycasterItem>(encode_id(type, id), raycaster, trafo, use_back_faces)); } case EType::Gizmo: { return m_gizmos.emplace_back(std::make_shared<SceneRaycasterItem>(encode_id(type, id), raycaster, trafo, use_back_faces)); }
case EType::FallbackGizmo: { return m_fallback_gizmos.emplace_back(std::make_shared<SceneRaycasterItem>(encode_id(type, id), raycaster, trafo, use_back_faces)); }
default: { assert(false); return nullptr; } default: { assert(false); return nullptr; }
}; };
} }
@ -60,6 +67,7 @@ void SceneRaycaster::remove_raycasters(EType type)
case EType::Bed: { m_bed.clear(); break; } case EType::Bed: { m_bed.clear(); break; }
case EType::Volume: { m_volumes.clear(); break; } case EType::Volume: { m_volumes.clear(); break; }
case EType::Gizmo: { m_gizmos.clear(); break; } case EType::Gizmo: { m_gizmos.clear(); break; }
case EType::FallbackGizmo: { m_fallback_gizmos.clear(); break; }
default: { break; } default: { break; }
}; };
} }
@ -84,28 +92,72 @@ void SceneRaycaster::remove_raycaster(std::shared_ptr<SceneRaycasterItem> item)
return; return;
} }
} }
for (auto it = m_fallback_gizmos.begin(); it != m_fallback_gizmos.end(); ++it) {
if (*it == item) {
m_fallback_gizmos.erase(it);
return;
}
}
} }
SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane) SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane) const
{ {
// helper class used to return currently selected volume as hit when overlapping with other volumes
// to allow the user to click and drag on a selected volume
class VolumeKeeper
{
std::optional<unsigned int> m_selected_volume_id;
Vec3f m_closest_hit_pos{ std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max() };
bool m_selected_volume_already_found{ false };
public:
VolumeKeeper() {
const Selection& selection = wxGetApp().plater()->get_selection();
if (selection.is_single_volume() || selection.is_single_modifier()) {
const GLVolume* volume = selection.get_first_volume();
if (!volume->is_wipe_tower && !volume->is_sla_pad() && !volume->is_sla_support())
m_selected_volume_id = *selection.get_volume_idxs().begin();
}
}
bool is_active() const { return m_selected_volume_id.has_value(); }
const Vec3f& get_closest_hit_pos() const { return m_closest_hit_pos; }
bool check_hit_result(const HitResult& hit) {
assert(is_active());
if (m_selected_volume_already_found && hit.type == SceneRaycaster::EType::Volume && hit.position.isApprox(m_closest_hit_pos))
return false;
if (hit.type == SceneRaycaster::EType::Volume)
m_selected_volume_already_found = *m_selected_volume_id == (unsigned int)decode_id(hit.type, hit.raycaster_id);
m_closest_hit_pos = hit.position;
return true;
}
};
VolumeKeeper volume_keeper;
double closest_hit_squared_distance = std::numeric_limits<double>::max(); double closest_hit_squared_distance = std::numeric_limits<double>::max();
auto is_closest = [&closest_hit_squared_distance](const Camera& camera, const Vec3f& hit) { auto is_closest = [&closest_hit_squared_distance, &volume_keeper](const Camera& camera, const Vec3f& hit) {
const double hit_squared_distance = (camera.get_position() - hit.cast<double>()).squaredNorm(); const double hit_squared_distance = (camera.get_position() - hit.cast<double>()).squaredNorm();
const bool ret = hit_squared_distance < closest_hit_squared_distance; bool ret = hit_squared_distance < closest_hit_squared_distance;
if (volume_keeper.is_active())
ret |= hit.isApprox(volume_keeper.get_closest_hit_pos());
if (ret) if (ret)
closest_hit_squared_distance = hit_squared_distance; closest_hit_squared_distance = hit_squared_distance;
return ret; return ret;
}; };
#if ENABLE_RAYCAST_PICKING_DEBUG #if ENABLE_RAYCAST_PICKING_DEBUG
m_last_hit.reset(); const_cast<std::optional<HitResult>*>(&m_last_hit)->reset();
#endif // ENABLE_RAYCAST_PICKING_DEBUG #endif // ENABLE_RAYCAST_PICKING_DEBUG
HitResult ret; HitResult ret;
auto test_raycasters = [this, is_closest, clipping_plane](EType type, const Vec2d& mouse_pos, const Camera& camera, HitResult& ret) { auto test_raycasters = [this, is_closest, clipping_plane, &volume_keeper](EType type, const Vec2d& mouse_pos, const Camera& camera, HitResult& ret) {
const ClippingPlane* clip_plane = (clipping_plane != nullptr && type == EType::Volume) ? clipping_plane : nullptr; const ClippingPlane* clip_plane = (clipping_plane != nullptr && type == EType::Volume) ? clipping_plane : nullptr;
std::vector<std::shared_ptr<SceneRaycasterItem>>* raycasters = get_raycasters(type); const std::vector<std::shared_ptr<SceneRaycasterItem>>* raycasters = get_raycasters(type);
const Vec3f camera_forward = camera.get_dir_forward().cast<float>(); const Vec3f camera_forward = camera.get_dir_forward().cast<float>();
HitResult current_hit = { type }; HitResult current_hit = { type };
for (std::shared_ptr<SceneRaycasterItem> item : *raycasters) { for (std::shared_ptr<SceneRaycasterItem> item : *raycasters) {
@ -117,8 +169,13 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came
if (item->get_raycaster()->closest_hit(mouse_pos, trafo, camera, current_hit.position, current_hit.normal, clip_plane)) { if (item->get_raycaster()->closest_hit(mouse_pos, trafo, camera, current_hit.position, current_hit.normal, clip_plane)) {
current_hit.position = (trafo * current_hit.position.cast<double>()).cast<float>(); current_hit.position = (trafo * current_hit.position.cast<double>()).cast<float>();
current_hit.normal = (trafo.matrix().block(0, 0, 3, 3).inverse().transpose() * current_hit.normal.cast<double>()).normalized().cast<float>(); current_hit.normal = (trafo.matrix().block(0, 0, 3, 3).inverse().transpose() * current_hit.normal.cast<double>()).normalized().cast<float>();
if (item->use_back_faces() || current_hit.normal.dot(camera_forward) < 0.0f){ if (item->use_back_faces() || current_hit.normal.dot(camera_forward) < 0.0f) {
if (is_closest(camera, current_hit.position)) { if (is_closest(camera, current_hit.position)) {
if (volume_keeper.is_active()) {
if (volume_keeper.check_hit_result(current_hit))
ret = current_hit;
}
else
ret = current_hit; ret = current_hit;
} }
} }
@ -129,6 +186,9 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came
if (!m_gizmos.empty()) if (!m_gizmos.empty())
test_raycasters(EType::Gizmo, mouse_pos, camera, ret); test_raycasters(EType::Gizmo, mouse_pos, camera, ret);
if (!m_fallback_gizmos.empty() && !ret.is_valid())
test_raycasters(EType::FallbackGizmo, mouse_pos, camera, ret);
if (!m_gizmos_on_top || !ret.is_valid()) { if (!m_gizmos_on_top || !ret.is_valid()) {
if (camera.is_looking_downward() && !m_bed.empty()) if (camera.is_looking_downward() && !m_bed.empty())
test_raycasters(EType::Bed, mouse_pos, camera, ret); test_raycasters(EType::Bed, mouse_pos, camera, ret);
@ -140,7 +200,7 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came
ret.raycaster_id = decode_id(ret.type, ret.raycaster_id); ret.raycaster_id = decode_id(ret.type, ret.raycaster_id);
#if ENABLE_RAYCAST_PICKING_DEBUG #if ENABLE_RAYCAST_PICKING_DEBUG
m_last_hit = ret; *const_cast<std::optional<HitResult>*>(&m_last_hit) = ret;
#endif // ENABLE_RAYCAST_PICKING_DEBUG #endif // ENABLE_RAYCAST_PICKING_DEBUG
return ret; return ret;
} }
@ -171,6 +231,39 @@ void SceneRaycaster::render_hit(const Camera& camera)
shader->stop_using(); shader->stop_using();
} }
size_t SceneRaycaster::active_beds_count() const {
size_t count = 0;
for (const auto& b : m_bed) {
if (b->is_active())
++count;
}
return count;
}
size_t SceneRaycaster::active_volumes_count() const {
size_t count = 0;
for (const auto& v : m_volumes) {
if (v->is_active())
++count;
}
return count;
}
size_t SceneRaycaster::active_gizmos_count() const {
size_t count = 0;
for (const auto& g : m_gizmos) {
if (g->is_active())
++count;
}
return count;
}
size_t SceneRaycaster::active_fallback_gizmos_count() const {
size_t count = 0;
for (const auto& g : m_fallback_gizmos) {
if (g->is_active())
++count;
}
return count;
}
#endif // ENABLE_RAYCAST_PICKING_DEBUG #endif // ENABLE_RAYCAST_PICKING_DEBUG
std::vector<std::shared_ptr<SceneRaycasterItem>>* SceneRaycaster::get_raycasters(EType type) std::vector<std::shared_ptr<SceneRaycasterItem>>* SceneRaycaster::get_raycasters(EType type)
@ -181,6 +274,22 @@ std::vector<std::shared_ptr<SceneRaycasterItem>>* SceneRaycaster::get_raycasters
case EType::Bed: { ret = &m_bed; break; } case EType::Bed: { ret = &m_bed; break; }
case EType::Volume: { ret = &m_volumes; break; } case EType::Volume: { ret = &m_volumes; break; }
case EType::Gizmo: { ret = &m_gizmos; break; } case EType::Gizmo: { ret = &m_gizmos; break; }
case EType::FallbackGizmo: { ret = &m_fallback_gizmos; break; }
default: { break; }
}
assert(ret != nullptr);
return ret;
}
const std::vector<std::shared_ptr<SceneRaycasterItem>>* SceneRaycaster::get_raycasters(EType type) const
{
const std::vector<std::shared_ptr<SceneRaycasterItem>>* ret = nullptr;
switch (type)
{
case EType::Bed: { ret = &m_bed; break; }
case EType::Volume: { ret = &m_volumes; break; }
case EType::Gizmo: { ret = &m_gizmos; break; }
case EType::FallbackGizmo: { ret = &m_fallback_gizmos; break; }
default: { break; } default: { break; }
} }
assert(ret != nullptr); assert(ret != nullptr);
@ -194,6 +303,7 @@ int SceneRaycaster::base_id(EType type)
case EType::Bed: { return int(EIdBase::Bed); } case EType::Bed: { return int(EIdBase::Bed); }
case EType::Volume: { return int(EIdBase::Volume); } case EType::Volume: { return int(EIdBase::Volume); }
case EType::Gizmo: { return int(EIdBase::Gizmo); } case EType::Gizmo: { return int(EIdBase::Gizmo); }
case EType::FallbackGizmo: { return int(EIdBase::FallbackGizmo); }
default: { break; } default: { break; }
}; };

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2022 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_SceneRaycaster_hpp_ #ifndef slic3r_SceneRaycaster_hpp_
#define slic3r_SceneRaycaster_hpp_ #define slic3r_SceneRaycaster_hpp_
@ -42,14 +46,16 @@ public:
None, None,
Bed, Bed,
Volume, Volume,
Gizmo Gizmo,
FallbackGizmo // Is used for gizmo grabbers which will be hit after all grabbers of Gizmo type
}; };
enum class EIdBase enum class EIdBase
{ {
Bed = 0, Bed = 0,
Volume = 1000, Volume = 1000,
Gizmo = 1000000 Gizmo = 1000000,
FallbackGizmo = 2000000
}; };
struct HitResult struct HitResult
@ -66,6 +72,7 @@ private:
std::vector<std::shared_ptr<SceneRaycasterItem>> m_bed; std::vector<std::shared_ptr<SceneRaycasterItem>> m_bed;
std::vector<std::shared_ptr<SceneRaycasterItem>> m_volumes; std::vector<std::shared_ptr<SceneRaycasterItem>> m_volumes;
std::vector<std::shared_ptr<SceneRaycasterItem>> m_gizmos; std::vector<std::shared_ptr<SceneRaycasterItem>> m_gizmos;
std::vector<std::shared_ptr<SceneRaycasterItem>> m_fallback_gizmos;
// When set to true, if checking gizmos returns a valid hit, // When set to true, if checking gizmos returns a valid hit,
// the search is not performed on other types // the search is not performed on other types
@ -87,10 +94,11 @@ public:
void remove_raycaster(std::shared_ptr<SceneRaycasterItem> item); void remove_raycaster(std::shared_ptr<SceneRaycasterItem> item);
std::vector<std::shared_ptr<SceneRaycasterItem>>* get_raycasters(EType type); std::vector<std::shared_ptr<SceneRaycasterItem>>* get_raycasters(EType type);
const std::vector<std::shared_ptr<SceneRaycasterItem>>* get_raycasters(EType type) const;
void set_gizmos_on_top(bool value) { m_gizmos_on_top = value; } void set_gizmos_on_top(bool value) { m_gizmos_on_top = value; }
HitResult hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane = nullptr); HitResult hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane = nullptr) const;
#if ENABLE_RAYCAST_PICKING_DEBUG #if ENABLE_RAYCAST_PICKING_DEBUG
void render_hit(const Camera& camera); void render_hit(const Camera& camera);
@ -98,11 +106,17 @@ public:
size_t beds_count() const { return m_bed.size(); } size_t beds_count() const { return m_bed.size(); }
size_t volumes_count() const { return m_volumes.size(); } size_t volumes_count() const { return m_volumes.size(); }
size_t gizmos_count() const { return m_gizmos.size(); } size_t gizmos_count() const { return m_gizmos.size(); }
size_t fallback_gizmos_count() const { return m_fallback_gizmos.size(); }
size_t active_beds_count() const;
size_t active_volumes_count() const;
size_t active_gizmos_count() const;
size_t active_fallback_gizmos_count() const;
#endif // ENABLE_RAYCAST_PICKING_DEBUG #endif // ENABLE_RAYCAST_PICKING_DEBUG
static int decode_id(EType type, int id);
private: private:
static int encode_id(EType type, int id); static int encode_id(EType type, int id);
static int decode_id(EType type, int id);
static int base_id(EType type); static int base_id(EType type);
}; };

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "libslic3r/libslic3r.h" #include "libslic3r/libslic3r.h"
#include "Selection.hpp" #include "Selection.hpp"
@ -710,6 +714,17 @@ bool Selection::contains_any_volume(const std::vector<unsigned int>& volume_idxs
return false; return false;
} }
bool Selection::contains_sinking_volumes(bool ignore_modifiers) const
{
for (const GLVolume* v : *m_volumes) {
if (!ignore_modifiers || !v->is_modifier) {
if (v->is_sinking())
return true;
}
}
return false;
}
bool Selection::matches(const std::vector<unsigned int>& volume_idxs) const bool Selection::matches(const std::vector<unsigned int>& volume_idxs) const
{ {
unsigned int count = 0; unsigned int count = 0;

View file

@ -1,7 +1,12 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Filip Sykala @Jony01, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GUI_Selection_hpp_ #ifndef slic3r_GUI_Selection_hpp_
#define slic3r_GUI_Selection_hpp_ #define slic3r_GUI_Selection_hpp_
#include "libslic3r/Geometry.hpp" #include "libslic3r/Geometry.hpp"
#include "GUI_Geometry.hpp"
#include "GLModel.hpp" #include "GLModel.hpp"
#include <set> #include <set>
@ -26,58 +31,6 @@ using ModelObjectPtrs = std::vector<ModelObject*>;
namespace GUI { namespace GUI {
class TransformationType
{
public:
enum Enum {
// Transforming in a world coordinate system
World = 0,
// Transforming in a local coordinate system
Local = 1,
// Absolute transformations, allowed in local coordinate system only.
Absolute = 0,
// Relative transformations, allowed in both local and world coordinate system.
Relative = 2,
// For group selection, the transformation is performed as if the group made a single solid body.
Joint = 0,
// For group selection, the transformation is performed on each object independently.
Independent = 4,
World_Relative_Joint = World | Relative | Joint,
World_Relative_Independent = World | Relative | Independent,
Local_Absolute_Joint = Local | Absolute | Joint,
Local_Absolute_Independent = Local | Absolute | Independent,
Local_Relative_Joint = Local | Relative | Joint,
Local_Relative_Independent = Local | Relative | Independent,
};
TransformationType() : m_value(World) {}
TransformationType(Enum value) : m_value(value) {}
TransformationType& operator=(Enum value) { m_value = value; return *this; }
Enum operator()() const { return m_value; }
bool has(Enum v) const { return ((unsigned int)m_value & (unsigned int)v) != 0; }
void set_world() { this->remove(Local); }
void set_local() { this->add(Local); }
void set_absolute() { this->remove(Relative); }
void set_relative() { this->add(Relative); }
void set_joint() { this->remove(Independent); }
void set_independent() { this->add(Independent); }
bool world() const { return !this->has(Local); }
bool local() const { return this->has(Local); }
bool absolute() const { return !this->has(Relative); }
bool relative() const { return this->has(Relative); }
bool joint() const { return !this->has(Independent); }
bool independent() const { return this->has(Independent); }
private:
void add(Enum v) { m_value = Enum((unsigned int)m_value | (unsigned int)v); }
void remove(Enum v) { m_value = Enum((unsigned int)m_value & (~(unsigned int)v)); }
Enum m_value;
};
class Selection class Selection
{ {
@ -307,6 +260,8 @@ public:
bool contains_all_volumes(const std::vector<unsigned int>& volume_idxs) const; bool contains_all_volumes(const std::vector<unsigned int>& volume_idxs) const;
// returns true if the selection contains at least one of the given indices // returns true if the selection contains at least one of the given indices
bool contains_any_volume(const std::vector<unsigned int>& volume_idxs) const; bool contains_any_volume(const std::vector<unsigned int>& volume_idxs) const;
// returns true if the selection contains any sinking volume
bool contains_sinking_volumes(bool ignore_modifiers = true) const;
// returns true if the selection contains all and only the given indices // returns true if the selection contains all and only the given indices
bool matches(const std::vector<unsigned int>& volume_idxs) const; bool matches(const std::vector<unsigned int>& volume_idxs) const;