Merge remote-tracking branch 'remotes/origin/fs_emboss'
This commit is contained in:
commit
9a682a10cb
124 changed files with 27968 additions and 7958 deletions
|
@ -90,20 +90,21 @@ inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over
|
|||
|
||||
// Finding a closest line, its closest point and squared distance to the closest point
|
||||
// Returns squared distance to the closest point or -1 if the input is empty.
|
||||
// or no closer point than max_sq_dist
|
||||
template<typename LineType, typename TreeType, typename VectorType>
|
||||
inline typename VectorType::Scalar squared_distance_to_indexed_lines(const std::vector<LineType> &lines,
|
||||
const TreeType &tree,
|
||||
const VectorType &point,
|
||||
size_t &hit_idx_out,
|
||||
Eigen::PlainObjectBase<VectorType> &hit_point_out)
|
||||
inline typename VectorType::Scalar squared_distance_to_indexed_lines(
|
||||
const std::vector<LineType> &lines,
|
||||
const TreeType &tree,
|
||||
const VectorType &point,
|
||||
size_t &hit_idx_out,
|
||||
Eigen::PlainObjectBase<VectorType> &hit_point_out,
|
||||
typename VectorType::Scalar max_sqr_dist = std::numeric_limits<typename VectorType::Scalar>::infinity())
|
||||
{
|
||||
using Scalar = typename VectorType::Scalar;
|
||||
using Scalar = typename VectorType::Scalar;
|
||||
if (tree.empty()) return Scalar(-1);
|
||||
auto distancer = detail::IndexedLinesDistancer<LineType, TreeType, VectorType>{lines, tree, point};
|
||||
return tree.empty() ?
|
||||
Scalar(-1) :
|
||||
AABBTreeIndirect::detail::squared_distance_to_indexed_primitives_recursive(distancer, size_t(0), Scalar(0),
|
||||
std::numeric_limits<Scalar>::infinity(),
|
||||
hit_idx_out, hit_point_out);
|
||||
return AABBTreeIndirect::detail::squared_distance_to_indexed_primitives_recursive(
|
||||
distancer, size_t(0), Scalar(0), max_sqr_dist, hit_idx_out, hit_point_out);
|
||||
}
|
||||
|
||||
// Returns all lines within the given radius limit
|
||||
|
|
|
@ -39,6 +39,7 @@ static const std::string VERSION_CHECK_URL = "https://files.prusa3d.com/wp-conte
|
|||
|
||||
const std::string AppConfig::SECTION_FILAMENTS = "filaments";
|
||||
const std::string AppConfig::SECTION_MATERIALS = "sla_materials";
|
||||
const std::string AppConfig::SECTION_EMBOSS_STYLE = "font";
|
||||
|
||||
void AppConfig::reset()
|
||||
{
|
||||
|
|
|
@ -167,6 +167,7 @@ public:
|
|||
|
||||
static const std::string SECTION_FILAMENTS;
|
||||
static const std::string SECTION_MATERIALS;
|
||||
static const std::string SECTION_EMBOSS_STYLE;
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
|
|
|
@ -53,7 +53,7 @@ public:
|
|||
PointClass size() const;
|
||||
double radius() const;
|
||||
void translate(coordf_t x, coordf_t y) { assert(this->defined); PointClass v(x, y); this->min += v; this->max += v; }
|
||||
void translate(const Vec2d &v) { this->min += v; this->max += v; }
|
||||
void translate(const PointClass &v) { this->min += v; this->max += v; }
|
||||
void offset(coordf_t delta);
|
||||
BoundingBoxBase<PointClass> inflated(coordf_t delta) const throw() { BoundingBoxBase<PointClass> out(*this); out.offset(delta); return out; }
|
||||
PointClass center() const;
|
||||
|
@ -174,6 +174,7 @@ public:
|
|||
BoundingBox rotated(double angle, const Point ¢er) const;
|
||||
void rotate(double angle) { (*this) = this->rotated(angle); }
|
||||
void rotate(double angle, const Point ¢er) { (*this) = this->rotated(angle, center); }
|
||||
bool intersects(const BoundingBox &other) const { return this->min(0) <= other.max(0) && this->max(0) >= other.min(0) && this->min(1) <= other.max(1) && this->max(1) >= other.min(1); }
|
||||
// Align the min corner to a grid of cell_size x cell_size cells,
|
||||
// to encompass the original bounding box.
|
||||
void align_to_grid(const coord_t cell_size);
|
||||
|
|
|
@ -19,6 +19,7 @@ set(SLIC3R_SOURCES
|
|||
pchheader.hpp
|
||||
AStar.hpp
|
||||
AABBTreeIndirect.hpp
|
||||
AABBTreeLines.hpp
|
||||
AABBMesh.hpp
|
||||
AABBMesh.cpp
|
||||
BoundingBox.cpp
|
||||
|
@ -42,9 +43,13 @@ set(SLIC3R_SOURCES
|
|||
EdgeGrid.hpp
|
||||
ElephantFootCompensation.cpp
|
||||
ElephantFootCompensation.hpp
|
||||
Emboss.cpp
|
||||
Emboss.hpp
|
||||
enum_bitmask.hpp
|
||||
ExPolygon.cpp
|
||||
ExPolygon.hpp
|
||||
ExPolygonsIndex.cpp
|
||||
ExPolygonsIndex.hpp
|
||||
Extruder.cpp
|
||||
Extruder.hpp
|
||||
ExtrusionEntity.cpp
|
||||
|
@ -178,6 +183,7 @@ set(SLIC3R_SOURCES
|
|||
Model.hpp
|
||||
ModelArrange.hpp
|
||||
ModelArrange.cpp
|
||||
#ModelVolumeType.hpp
|
||||
MultiMaterialSegmentation.cpp
|
||||
MultiMaterialSegmentation.hpp
|
||||
MeshNormals.hpp
|
||||
|
@ -194,6 +200,8 @@ set(SLIC3R_SOURCES
|
|||
MutablePriorityQueue.hpp
|
||||
NormalUtils.cpp
|
||||
NormalUtils.hpp
|
||||
NSVGUtils.cpp
|
||||
NSVGUtils.hpp
|
||||
ObjectID.cpp
|
||||
ObjectID.hpp
|
||||
PerimeterGenerator.cpp
|
||||
|
@ -266,6 +274,7 @@ set(SLIC3R_SOURCES
|
|||
Technologies.hpp
|
||||
Tesselate.cpp
|
||||
Tesselate.hpp
|
||||
TextConfiguration.hpp
|
||||
TreeSupport.cpp
|
||||
TreeSupport.hpp
|
||||
TreeModelVolumes.cpp
|
||||
|
@ -280,6 +289,8 @@ set(SLIC3R_SOURCES
|
|||
Utils.hpp
|
||||
Time.cpp
|
||||
Time.hpp
|
||||
Timer.cpp
|
||||
Timer.hpp
|
||||
Thread.cpp
|
||||
Thread.hpp
|
||||
TriangleSelector.cpp
|
||||
|
@ -403,8 +414,14 @@ cmake_policy(SET CMP0011 NEW)
|
|||
find_package(CGAL REQUIRED)
|
||||
cmake_policy(POP)
|
||||
|
||||
add_library(libslic3r_cgal STATIC MeshBoolean.cpp MeshBoolean.hpp TryCatchSignal.hpp
|
||||
TryCatchSignal.cpp Geometry/VoronoiUtilsCgal.hpp Geometry/VoronoiUtilsCgal.cpp)
|
||||
add_library(libslic3r_cgal STATIC
|
||||
CutSurface.hpp CutSurface.cpp
|
||||
Geometry/VoronoiUtilsCgal.hpp Geometry/VoronoiUtilsCgal.cpp
|
||||
IntersectionPoints.hpp IntersectionPoints.cpp
|
||||
MeshBoolean.hpp MeshBoolean.cpp
|
||||
TryCatchSignal.hpp TryCatchSignal.cpp
|
||||
Triangulation.hpp Triangulation.cpp
|
||||
)
|
||||
target_include_directories(libslic3r_cgal PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
# Reset compile options of libslic3r_cgal. Despite it being linked privately, CGAL options
|
||||
|
@ -476,6 +493,7 @@ if (APPLE)
|
|||
# This flag prevents the need for minimum SDK version 10.14
|
||||
# currently, PS targets v10.12
|
||||
target_compile_options(libslic3r PUBLIC "-fno-aligned-allocation")
|
||||
target_compile_options(libslic3r_cgal PUBLIC "-fno-aligned-allocation")
|
||||
endif ()
|
||||
|
||||
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
||||
|
|
3990
src/libslic3r/CutSurface.cpp
Normal file
3990
src/libslic3r/CutSurface.cpp
Normal file
File diff suppressed because it is too large
Load diff
74
src/libslic3r/CutSurface.hpp
Normal file
74
src/libslic3r/CutSurface.hpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
#ifndef slic3r_CutSurface_hpp_
|
||||
#define slic3r_CutSurface_hpp_
|
||||
|
||||
#include <vector>
|
||||
#include <admesh/stl.h> // indexed_triangle_set
|
||||
#include "ExPolygon.hpp"
|
||||
#include "Emboss.hpp" // IProjection
|
||||
|
||||
namespace Slic3r{
|
||||
|
||||
/// <summary>
|
||||
/// Represents cutted surface from object
|
||||
/// Extend index triangle set by outlines
|
||||
/// </summary>
|
||||
struct SurfaceCut : public indexed_triangle_set
|
||||
{
|
||||
// vertex indices(index to mesh vertices)
|
||||
using Index = unsigned int;
|
||||
using Contour = std::vector<Index>;
|
||||
using Contours = std::vector<Contour>;
|
||||
// list of circulated open surface
|
||||
Contours contours;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Cut surface shape from models.
|
||||
/// </summary>
|
||||
/// <param name="shapes">Multiple shape to cut from model</param>
|
||||
/// <param name="models">Multi mesh to cut, need to be in same coordinate system</param>
|
||||
/// <param name="projection">Define transformation 2d shape into 3d</param>
|
||||
/// <param name="projection_ratio">Define ideal ratio between front and back projection to cut
|
||||
/// 0 .. means use closest to front projection
|
||||
/// 1 .. means use closest to back projection
|
||||
/// value from <0, 1>
|
||||
/// </param>
|
||||
/// <returns>Cutted surface from model</returns>
|
||||
SurfaceCut cut_surface(const ExPolygons &shapes,
|
||||
const std::vector<indexed_triangle_set> &models,
|
||||
const Emboss::IProjection &projection,
|
||||
float projection_ratio);
|
||||
|
||||
/// <summary>
|
||||
/// Create model from surface cuts by projection
|
||||
/// </summary>
|
||||
/// <param name="cut">Surface from model with outlines</param>
|
||||
/// <param name="projection">Way of emboss</param>
|
||||
/// <returns>Mesh</returns>
|
||||
indexed_triangle_set cut2model(const SurfaceCut &cut,
|
||||
const Emboss::IProject3d &projection);
|
||||
|
||||
/// <summary>
|
||||
/// Separate (A)rea (o)f (I)nterest .. AoI from model
|
||||
/// NOTE: Only 2d filtration, do not filtrate by Z coordinate
|
||||
/// </summary>
|
||||
/// <param name="its">Input model</param>
|
||||
/// <param name="bb">Bounding box to project into space</param>
|
||||
/// <param name="projection">Define tranformation of BB into space</param>
|
||||
/// <returns>Triangles lay at least partialy inside of projected Bounding box</returns>
|
||||
indexed_triangle_set its_cut_AoI(const indexed_triangle_set &its,
|
||||
const BoundingBox &bb,
|
||||
const Emboss::IProjection &projection);
|
||||
|
||||
/// <summary>
|
||||
/// Separate triangles by mask
|
||||
/// </summary>
|
||||
/// <param name="its">Input model</param>
|
||||
/// <param name="mask">Mask - same size as its::indices</param>
|
||||
/// <returns>Copy of indices by mask(with their vertices)</returns>
|
||||
indexed_triangle_set its_mask(const indexed_triangle_set &its, const std::vector<bool> &mask);
|
||||
|
||||
bool corefine_test(const std::string &model_path, const std::string &shape_path);
|
||||
|
||||
} // namespace Slic3r
|
||||
#endif // slic3r_CutSurface_hpp_
|
1294
src/libslic3r/Emboss.cpp
Normal file
1294
src/libslic3r/Emboss.cpp
Normal file
File diff suppressed because it is too large
Load diff
349
src/libslic3r/Emboss.hpp
Normal file
349
src/libslic3r/Emboss.hpp
Normal file
|
@ -0,0 +1,349 @@
|
|||
#ifndef slic3r_Emboss_hpp_
|
||||
#define slic3r_Emboss_hpp_
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <admesh/stl.h> // indexed_triangle_set
|
||||
#include "Polygon.hpp"
|
||||
#include "ExPolygon.hpp"
|
||||
#include "TextConfiguration.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
/// <summary>
|
||||
/// class with only static function add ability to engraved OR raised
|
||||
/// text OR polygons onto model surface
|
||||
/// </summary>
|
||||
namespace Emboss
|
||||
{
|
||||
// every glyph's shape point is divided by SHAPE_SCALE - increase precission of fixed point value
|
||||
// stored in fonts (to be able represents curve by sequence of lines)
|
||||
static constexpr double SHAPE_SCALE = 0.001; // SCALING_FACTOR promile is fine enough
|
||||
|
||||
/// <summary>
|
||||
/// Collect fonts registred inside OS
|
||||
/// </summary>
|
||||
/// <returns>OS registred TTF font files(full path) with names</returns>
|
||||
EmbossStyles get_font_list();
|
||||
#ifdef _WIN32
|
||||
EmbossStyles get_font_list_by_register();
|
||||
EmbossStyles get_font_list_by_enumeration();
|
||||
EmbossStyles get_font_list_by_folder();
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// OS dependent function to get location of font by its name descriptor
|
||||
/// </summary>
|
||||
/// <param name="font_face_name">Unique identificator for font</param>
|
||||
/// <returns>File path to font when found</returns>
|
||||
std::optional<std::wstring> get_font_path(const std::wstring &font_face_name);
|
||||
|
||||
// description of one letter
|
||||
struct Glyph
|
||||
{
|
||||
// NOTE: shape is scaled by SHAPE_SCALE
|
||||
// to be able store points without floating points
|
||||
ExPolygons shape;
|
||||
|
||||
// values are in font points
|
||||
int advance_width=0, left_side_bearing=0;
|
||||
};
|
||||
// cache for glyph by unicode
|
||||
using Glyphs = std::map<int, Glyph>;
|
||||
|
||||
/// <summary>
|
||||
/// keep information from file about font
|
||||
/// (store file data itself)
|
||||
/// + cache data readed from buffer
|
||||
/// </summary>
|
||||
struct FontFile
|
||||
{
|
||||
// loaded data from font file
|
||||
// must store data size for imgui rasterization
|
||||
// To not store data on heap and To prevent unneccesary copy
|
||||
// data are stored inside unique_ptr
|
||||
std::unique_ptr<std::vector<unsigned char>> data;
|
||||
|
||||
struct Info
|
||||
{
|
||||
// vertical position is "scale*(ascent - descent + lineGap)"
|
||||
int ascent, descent, linegap;
|
||||
|
||||
// for convert font units to pixel
|
||||
int unit_per_em;
|
||||
};
|
||||
// info for each font in data
|
||||
std::vector<Info> infos;
|
||||
|
||||
FontFile(std::unique_ptr<std::vector<unsigned char>> data,
|
||||
std::vector<Info> &&infos)
|
||||
: data(std::move(data)), infos(std::move(infos))
|
||||
{
|
||||
assert(this->data != nullptr);
|
||||
assert(!this->data->empty());
|
||||
}
|
||||
|
||||
bool operator==(const FontFile &other) const {
|
||||
if (data->size() != other.data->size())
|
||||
return false;
|
||||
//if(*data != *other.data) return false;
|
||||
for (size_t i = 0; i < infos.size(); i++)
|
||||
if (infos[i].ascent != other.infos[i].ascent ||
|
||||
infos[i].descent == other.infos[i].descent ||
|
||||
infos[i].linegap == other.infos[i].linegap)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Add caching for shape of glyphs
|
||||
/// </summary>
|
||||
struct FontFileWithCache
|
||||
{
|
||||
// Pointer on data of the font file
|
||||
std::shared_ptr<const FontFile> font_file;
|
||||
|
||||
// Cache for glyph shape
|
||||
// IMPORTANT: accessible only in plater job thread !!!
|
||||
// main thread only clear cache by set to another shared_ptr
|
||||
std::shared_ptr<Emboss::Glyphs> cache;
|
||||
|
||||
FontFileWithCache() : font_file(nullptr), cache(nullptr) {}
|
||||
FontFileWithCache(std::unique_ptr<FontFile> font_file)
|
||||
: font_file(std::move(font_file))
|
||||
, cache(std::make_shared<Emboss::Glyphs>())
|
||||
{}
|
||||
bool has_value() const { return font_file != nullptr && cache != nullptr; }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Load font file into buffer
|
||||
/// </summary>
|
||||
/// <param name="file_path">Location of .ttf or .ttc font file</param>
|
||||
/// <returns>Font object when loaded.</returns>
|
||||
std::unique_ptr<FontFile> create_font_file(const char *file_path);
|
||||
// data = raw file data
|
||||
std::unique_ptr<FontFile> create_font_file(std::unique_ptr<std::vector<unsigned char>> data);
|
||||
#ifdef _WIN32
|
||||
// fix for unknown pointer HFONT
|
||||
using HFONT = void*;
|
||||
void * can_load(HFONT hfont);
|
||||
std::unique_ptr<FontFile> create_font_file(HFONT hfont);
|
||||
#endif // _WIN32
|
||||
|
||||
/// <summary>
|
||||
/// convert letter into polygons
|
||||
/// </summary>
|
||||
/// <param name="font">Define fonts</param>
|
||||
/// <param name="font_index">Index of font in collection</param>
|
||||
/// <param name="letter">One character defined by unicode codepoint</param>
|
||||
/// <param name="flatness">Precision of lettter outline curve in conversion to lines</param>
|
||||
/// <returns>inner polygon cw(outer ccw)</returns>
|
||||
std::optional<Glyph> letter2glyph(const FontFile &font, unsigned int font_index, int letter, float flatness);
|
||||
|
||||
/// <summary>
|
||||
/// Convert text into polygons
|
||||
/// </summary>
|
||||
/// <param name="font">Define fonts + cache, which could extend</param>
|
||||
/// <param name="text">Characters to convert</param>
|
||||
/// <param name="font_prop">User defined property of the font</param>
|
||||
/// <param name="was_canceled">Way to interupt processing</param>
|
||||
/// <returns>Inner polygon cw(outer ccw)</returns>
|
||||
ExPolygons text2shapes(FontFileWithCache &font, const char *text, const FontProp &font_prop, std::function<bool()> was_canceled = nullptr);
|
||||
|
||||
/// <summary>
|
||||
/// Fix intersections and self intersections in polygons glyph shape
|
||||
/// </summary>
|
||||
/// <param name="shape">Input shape to heal</param>
|
||||
/// <returns>Healed shapes</returns>
|
||||
ExPolygons heal_shape(const Polygons &shape);
|
||||
|
||||
/// <summary>
|
||||
/// NOTE: call Slic3r::union_ex before this call
|
||||
///
|
||||
/// Heal (read: Fix) issues in expolygons:
|
||||
/// - self intersections
|
||||
/// - duplicit points
|
||||
/// - points close to line segments
|
||||
/// </summary>
|
||||
/// <param name="shape">In/Out shape to heal</param>
|
||||
/// <param name="max_iteration">Heal could create another issue,
|
||||
/// After healing it is checked again until shape is good or maximal count of iteration</param>
|
||||
/// <returns>True when shapes is good otherwise False</returns>
|
||||
bool heal_shape(ExPolygons &shape, unsigned max_iteration = 10);
|
||||
|
||||
/// <summary>
|
||||
/// Divide line segments in place near to point
|
||||
/// (which could lead to self intersection due to preccision)
|
||||
/// Remove same neighbors
|
||||
/// Note: Possible part of heal shape
|
||||
/// </summary>
|
||||
/// <param name="expolygons">Expolygon to edit</param>
|
||||
/// <param name="distance">(epsilon)Euclidean distance from point to line which divide line</param>
|
||||
/// <returns>True when some division was made otherwise false</returns>
|
||||
bool divide_segments_for_close_point(ExPolygons &expolygons, double distance);
|
||||
|
||||
/// <summary>
|
||||
/// Use data from font property to modify transformation
|
||||
/// </summary>
|
||||
/// <param name="font_prop">Z-move as surface distance(FontProp::distance)
|
||||
/// Z-rotation as angle to Y axis(FontProp::angle)</param>
|
||||
/// <param name="transformation">In / Out transformation to modify by property</param>
|
||||
void apply_transformation(const FontProp &font_prop, Transform3d &transformation);
|
||||
|
||||
/// <summary>
|
||||
/// Read information from naming table of font file
|
||||
/// search for italic (or oblique), bold italic (or bold oblique)
|
||||
/// </summary>
|
||||
/// <param name="font">Selector of font</param>
|
||||
/// <param name="font_index">Index of font in collection</param>
|
||||
/// <returns>True when the font description contains italic/obligue otherwise False</returns>
|
||||
bool is_italic(const FontFile &font, unsigned int font_index);
|
||||
|
||||
/// <summary>
|
||||
/// Create unique character set from string with filtered from text with only character from font
|
||||
/// </summary>
|
||||
/// <param name="text">Source vector of glyphs</param>
|
||||
/// <param name="font">Font descriptor</param>
|
||||
/// <param name="font_index">Define font in collection</param>
|
||||
/// <param name="exist_unknown">True when text contain glyph unknown in font</param>
|
||||
/// <returns>Unique set of character from text contained in font</returns>
|
||||
std::string create_range_text(const std::string &text, const FontFile &font, unsigned int font_index, bool* exist_unknown = nullptr);
|
||||
|
||||
/// <summary>
|
||||
/// Calculate scale for glyph shape convert from shape points to mm
|
||||
/// </summary>
|
||||
/// <param name="fp">Property of font</param>
|
||||
/// <param name="ff">Font data</param>
|
||||
/// <returns>Conversion to mm</returns>
|
||||
double get_shape_scale(const FontProp &fp, const FontFile &ff);
|
||||
|
||||
/// <summary>
|
||||
/// Project spatial point
|
||||
/// </summary>
|
||||
class IProject3d
|
||||
{
|
||||
public:
|
||||
virtual ~IProject3d() = default;
|
||||
/// <summary>
|
||||
/// Move point with respect to projection direction
|
||||
/// e.g. Orthogonal projection will move with point by direction
|
||||
/// e.g. Spherical projection need to use center of projection
|
||||
/// </summary>
|
||||
/// <param name="point">Spatial point coordinate</param>
|
||||
/// <returns>Projected spatial point</returns>
|
||||
virtual Vec3d project(const Vec3d &point) const = 0;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Project 2d point into space
|
||||
/// Could be plane, sphere, cylindric, ...
|
||||
/// </summary>
|
||||
class IProjection : public IProject3d
|
||||
{
|
||||
public:
|
||||
virtual ~IProjection() = default;
|
||||
|
||||
/// <summary>
|
||||
/// convert 2d point to 3d points
|
||||
/// </summary>
|
||||
/// <param name="p">2d coordinate</param>
|
||||
/// <returns>
|
||||
/// first - front spatial point
|
||||
/// second - back spatial point
|
||||
/// </returns>
|
||||
virtual std::pair<Vec3d, Vec3d> create_front_back(const Point &p) const = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Back projection
|
||||
/// </summary>
|
||||
/// <param name="p">Point to project</param>
|
||||
/// <param name="depth">[optional] Depth of 2d projected point. Be careful number is in 2d scale</param>
|
||||
/// <returns>Uprojected point when it is possible</returns>
|
||||
virtual std::optional<Vec2d> unproject(const Vec3d &p, double * depth = nullptr) const = 0;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Create triangle model for text
|
||||
/// </summary>
|
||||
/// <param name="shape2d">text or image</param>
|
||||
/// <param name="projection">Define transformation from 2d to 3d(orientation, position, scale, ...)</param>
|
||||
/// <returns>Projected shape into space</returns>
|
||||
indexed_triangle_set polygons2model(const ExPolygons &shape2d, const IProjection& projection);
|
||||
|
||||
/// <summary>
|
||||
/// Create transformation for emboss text object to lay on surface point
|
||||
/// </summary>
|
||||
/// <param name="position">Position of surface point</param>
|
||||
/// <param name="normal">Normal of surface point</param>
|
||||
/// <param name="up_limit">Is compared with normal.z to suggest up direction</param>
|
||||
/// <returns>Transformation onto surface point</returns>
|
||||
Transform3d create_transformation_onto_surface(
|
||||
const Vec3f &position, const Vec3f &normal, float up_limit = 0.9f);
|
||||
|
||||
class ProjectZ : public IProjection
|
||||
{
|
||||
public:
|
||||
ProjectZ(double depth) : m_depth(depth) {}
|
||||
// Inherited via IProject
|
||||
std::pair<Vec3d, Vec3d> create_front_back(const Point &p) const override;
|
||||
Vec3d project(const Vec3d &point) const override;
|
||||
std::optional<Vec2d> unproject(const Vec3d &p, double * depth = nullptr) const override;
|
||||
double m_depth;
|
||||
};
|
||||
|
||||
class ProjectScale : public IProjection
|
||||
{
|
||||
std::unique_ptr<IProjection> core;
|
||||
double m_scale;
|
||||
public:
|
||||
ProjectScale(std::unique_ptr<IProjection> core, double scale)
|
||||
: core(std::move(core)), m_scale(scale)
|
||||
{}
|
||||
|
||||
// Inherited via IProject
|
||||
std::pair<Vec3d, Vec3d> create_front_back(const Point &p) const override
|
||||
{
|
||||
auto res = core->create_front_back(p);
|
||||
return std::make_pair(res.first * m_scale, res.second * m_scale);
|
||||
}
|
||||
Vec3d project(const Vec3d &point) const override{
|
||||
return core->project(point);
|
||||
}
|
||||
std::optional<Vec2d> unproject(const Vec3d &p, double *depth = nullptr) const override {
|
||||
auto res = core->unproject(p / m_scale, depth);
|
||||
if (depth != nullptr) *depth *= m_scale;
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
class OrthoProject3d : public Emboss::IProject3d
|
||||
{
|
||||
// size and direction of emboss for ortho projection
|
||||
Vec3d m_direction;
|
||||
public:
|
||||
OrthoProject3d(Vec3d direction) : m_direction(direction) {}
|
||||
Vec3d project(const Vec3d &point) const override{ return point + m_direction;}
|
||||
};
|
||||
|
||||
class OrthoProject: public Emboss::IProjection {
|
||||
Transform3d m_matrix;
|
||||
// size and direction of emboss for ortho projection
|
||||
Vec3d m_direction;
|
||||
Transform3d m_matrix_inv;
|
||||
public:
|
||||
OrthoProject(Transform3d matrix, Vec3d direction)
|
||||
: m_matrix(matrix), m_direction(direction), m_matrix_inv(matrix.inverse())
|
||||
{}
|
||||
// Inherited via IProject
|
||||
std::pair<Vec3d, Vec3d> create_front_back(const Point &p) const override;
|
||||
Vec3d project(const Vec3d &point) const override;
|
||||
std::optional<Vec2d> unproject(const Vec3d &p, double * depth = nullptr) const override;
|
||||
};
|
||||
} // namespace Emboss
|
||||
|
||||
} // namespace Slic3r
|
||||
#endif // slic3r_Emboss_hpp_
|
|
@ -85,6 +85,25 @@ public:
|
|||
inline bool operator==(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour == rhs.contour && lhs.holes == rhs.holes; }
|
||||
inline bool operator!=(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour != rhs.contour || lhs.holes != rhs.holes; }
|
||||
|
||||
inline size_t count_points(const ExPolygons &expolys)
|
||||
{
|
||||
size_t n_points = 0;
|
||||
for (const auto &expoly : expolys) {
|
||||
n_points += expoly.contour.points.size();
|
||||
for (const auto &hole : expoly.holes)
|
||||
n_points += hole.points.size();
|
||||
}
|
||||
return n_points;
|
||||
}
|
||||
|
||||
inline size_t count_points(const ExPolygon &expoly)
|
||||
{
|
||||
size_t n_points = expoly.contour.points.size();
|
||||
for (const auto &hole : expoly.holes)
|
||||
n_points += hole.points.size();
|
||||
return n_points;
|
||||
}
|
||||
|
||||
// Count a nuber of polygons stored inside the vector of expolygons.
|
||||
// Useful for allocating space for polygons when converting expolygons to polygons.
|
||||
inline size_t number_polygons(const ExPolygons &expolys)
|
||||
|
@ -97,11 +116,8 @@ inline size_t number_polygons(const ExPolygons &expolys)
|
|||
|
||||
inline Lines to_lines(const ExPolygon &src)
|
||||
{
|
||||
size_t n_lines = src.contour.points.size();
|
||||
for (size_t i = 0; i < src.holes.size(); ++ i)
|
||||
n_lines += src.holes[i].points.size();
|
||||
Lines lines;
|
||||
lines.reserve(n_lines);
|
||||
lines.reserve(count_points(src));
|
||||
for (size_t i = 0; i <= src.holes.size(); ++ i) {
|
||||
const Polygon &poly = (i == 0) ? src.contour : src.holes[i - 1];
|
||||
for (Points::const_iterator it = poly.points.begin(); it != poly.points.end()-1; ++it)
|
||||
|
@ -113,14 +129,8 @@ inline Lines to_lines(const ExPolygon &src)
|
|||
|
||||
inline Lines to_lines(const ExPolygons &src)
|
||||
{
|
||||
size_t n_lines = 0;
|
||||
for (ExPolygons::const_iterator it_expoly = src.begin(); it_expoly != src.end(); ++ it_expoly) {
|
||||
n_lines += it_expoly->contour.points.size();
|
||||
for (size_t i = 0; i < it_expoly->holes.size(); ++ i)
|
||||
n_lines += it_expoly->holes[i].points.size();
|
||||
}
|
||||
Lines lines;
|
||||
lines.reserve(n_lines);
|
||||
lines.reserve(count_points(src));
|
||||
for (ExPolygons::const_iterator it_expoly = src.begin(); it_expoly != src.end(); ++ it_expoly) {
|
||||
for (size_t i = 0; i <= it_expoly->holes.size(); ++ i) {
|
||||
const Points &points = ((i == 0) ? it_expoly->contour : it_expoly->holes[i - 1]).points;
|
||||
|
@ -132,16 +142,40 @@ inline Lines to_lines(const ExPolygons &src)
|
|||
return lines;
|
||||
}
|
||||
|
||||
inline std::vector<Linef> to_unscaled_linesf(const ExPolygons &src)
|
||||
// Line is from point index(see to_points) to next point.
|
||||
// Next point of last point in polygon is first polygon point.
|
||||
inline Linesf to_linesf(const ExPolygons &src, uint32_t count_lines = 0)
|
||||
{
|
||||
size_t n_lines = 0;
|
||||
for (ExPolygons::const_iterator it_expoly = src.begin(); it_expoly != src.end(); ++ it_expoly) {
|
||||
n_lines += it_expoly->contour.points.size();
|
||||
for (size_t i = 0; i < it_expoly->holes.size(); ++ i)
|
||||
n_lines += it_expoly->holes[i].points.size();
|
||||
assert(count_lines == 0 || count_lines == count_points(src));
|
||||
if (count_lines == 0) count_lines = count_points(src);
|
||||
Linesf lines;
|
||||
lines.reserve(count_lines);
|
||||
Vec2d prev_pd;
|
||||
auto to_lines = [&lines, &prev_pd](const Points &pts) {
|
||||
assert(pts.size() >= 3);
|
||||
if (pts.size() < 2) return;
|
||||
bool is_first = true;
|
||||
for (const Point &p : pts) {
|
||||
Vec2d pd = p.cast<double>();
|
||||
if (is_first) is_first = false;
|
||||
else lines.emplace_back(prev_pd, pd);
|
||||
prev_pd = pd;
|
||||
}
|
||||
lines.emplace_back(prev_pd, pts.front().cast<double>());
|
||||
};
|
||||
for (const ExPolygon& expoly: src) {
|
||||
to_lines(expoly.contour.points);
|
||||
for (const Polygon &hole : expoly.holes)
|
||||
to_lines(hole.points);
|
||||
}
|
||||
std::vector<Linef> lines;
|
||||
lines.reserve(n_lines);
|
||||
assert(lines.size() == count_lines);
|
||||
return lines;
|
||||
}
|
||||
|
||||
inline Linesf to_unscaled_linesf(const ExPolygons &src)
|
||||
{
|
||||
Linesf lines;
|
||||
lines.reserve(count_points(src));
|
||||
for (ExPolygons::const_iterator it_expoly = src.begin(); it_expoly != src.end(); ++ it_expoly) {
|
||||
for (size_t i = 0; i <= it_expoly->holes.size(); ++ i) {
|
||||
const Points &points = ((i == 0) ? it_expoly->contour : it_expoly->holes[i - 1]).points;
|
||||
|
@ -159,6 +193,19 @@ inline std::vector<Linef> to_unscaled_linesf(const ExPolygons &src)
|
|||
}
|
||||
|
||||
|
||||
inline Points to_points(const ExPolygons &src)
|
||||
{
|
||||
Points points;
|
||||
size_t count = count_points(src);
|
||||
points.reserve(count);
|
||||
for (const ExPolygon &expolygon : src) {
|
||||
append(points, expolygon.contour.points);
|
||||
for (const Polygon &hole : expolygon.holes)
|
||||
append(points, hole.points);
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
inline Polylines to_polylines(const ExPolygon &src)
|
||||
{
|
||||
Polylines polylines;
|
||||
|
@ -278,8 +325,9 @@ inline Polygons to_polygons(ExPolygon &&src)
|
|||
Polygons polygons;
|
||||
polygons.reserve(src.holes.size() + 1);
|
||||
polygons.push_back(std::move(src.contour));
|
||||
std::move(std::begin(src.holes), std::end(src.holes), std::back_inserter(polygons));
|
||||
src.holes.clear();
|
||||
polygons.insert(polygons.end(),
|
||||
std::make_move_iterator(src.holes.begin()),
|
||||
std::make_move_iterator(src.holes.end()));
|
||||
return polygons;
|
||||
}
|
||||
|
||||
|
@ -287,10 +335,11 @@ inline Polygons to_polygons(ExPolygons &&src)
|
|||
{
|
||||
Polygons polygons;
|
||||
polygons.reserve(number_polygons(src));
|
||||
for (ExPolygons::iterator it = src.begin(); it != src.end(); ++it) {
|
||||
polygons.push_back(std::move(it->contour));
|
||||
std::move(std::begin(it->holes), std::end(it->holes), std::back_inserter(polygons));
|
||||
it->holes.clear();
|
||||
for (ExPolygon& expoly: src) {
|
||||
polygons.push_back(std::move(expoly.contour));
|
||||
polygons.insert(polygons.end(),
|
||||
std::make_move_iterator(expoly.holes.begin()),
|
||||
std::make_move_iterator(expoly.holes.end()));
|
||||
}
|
||||
return polygons;
|
||||
}
|
||||
|
@ -315,11 +364,8 @@ inline ExPolygons to_expolygons(Polygons &&polys)
|
|||
|
||||
inline Points to_points(const ExPolygon &expoly)
|
||||
{
|
||||
size_t cnt = expoly.contour.size();
|
||||
for (const Polygon &hole : expoly.holes)
|
||||
cnt += hole.size();
|
||||
Points out;
|
||||
out.reserve(cnt);
|
||||
out.reserve(count_points(expoly));
|
||||
append(out, expoly.contour.points);
|
||||
for (const Polygon &hole : expoly.holes)
|
||||
append(out, hole.points);
|
||||
|
@ -345,18 +391,20 @@ inline void polygons_append(Polygons &dst, const ExPolygons &src)
|
|||
inline void polygons_append(Polygons &dst, ExPolygon &&src)
|
||||
{
|
||||
dst.reserve(dst.size() + src.holes.size() + 1);
|
||||
dst.push_back(std::move(src.contour));
|
||||
std::move(std::begin(src.holes), std::end(src.holes), std::back_inserter(dst));
|
||||
src.holes.clear();
|
||||
dst.push_back(std::move(src.contour));
|
||||
dst.insert(dst.end(),
|
||||
std::make_move_iterator(src.holes.begin()),
|
||||
std::make_move_iterator(src.holes.end()));
|
||||
}
|
||||
|
||||
inline void polygons_append(Polygons &dst, ExPolygons &&src)
|
||||
{
|
||||
dst.reserve(dst.size() + number_polygons(src));
|
||||
for (ExPolygons::iterator it = src.begin(); it != src.end(); ++ it) {
|
||||
dst.push_back(std::move(it->contour));
|
||||
std::move(std::begin(it->holes), std::end(it->holes), std::back_inserter(dst));
|
||||
it->holes.clear();
|
||||
for (ExPolygon& expoly: src) {
|
||||
dst.push_back(std::move(expoly.contour));
|
||||
dst.insert(dst.end(),
|
||||
std::make_move_iterator(expoly.holes.begin()),
|
||||
std::make_move_iterator(expoly.holes.end()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -370,8 +418,9 @@ inline void expolygons_append(ExPolygons &dst, ExPolygons &&src)
|
|||
if (dst.empty()) {
|
||||
dst = std::move(src);
|
||||
} else {
|
||||
std::move(std::begin(src), std::end(src), std::back_inserter(dst));
|
||||
src.clear();
|
||||
dst.insert(dst.end(),
|
||||
std::make_move_iterator(src.begin()),
|
||||
std::make_move_iterator(src.end()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
82
src/libslic3r/ExPolygonsIndex.cpp
Normal file
82
src/libslic3r/ExPolygonsIndex.cpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
#include "ExPolygonsIndex.hpp"
|
||||
using namespace Slic3r;
|
||||
|
||||
// IMPROVE: use one dimensional vector for polygons offset with searching by std::lower_bound
|
||||
ExPolygonsIndices::ExPolygonsIndices(const ExPolygons &shapes)
|
||||
{
|
||||
// prepare offsets
|
||||
m_offsets.reserve(shapes.size());
|
||||
uint32_t offset = 0;
|
||||
for (const ExPolygon &shape : shapes) {
|
||||
assert(!shape.contour.points.empty());
|
||||
std::vector<uint32_t> shape_offsets;
|
||||
shape_offsets.reserve(shape.holes.size() + 1);
|
||||
shape_offsets.push_back(offset);
|
||||
offset += shape.contour.points.size();
|
||||
for (const Polygon &hole: shape.holes) {
|
||||
shape_offsets.push_back(offset);
|
||||
offset += hole.points.size();
|
||||
}
|
||||
m_offsets.push_back(std::move(shape_offsets));
|
||||
}
|
||||
m_count = offset;
|
||||
}
|
||||
|
||||
uint32_t ExPolygonsIndices::cvt(const ExPolygonsIndex &id) const
|
||||
{
|
||||
assert(id.expolygons_index < m_offsets.size());
|
||||
const std::vector<uint32_t> &shape_offset = m_offsets[id.expolygons_index];
|
||||
assert(id.polygon_index < shape_offset.size());
|
||||
uint32_t res = shape_offset[id.polygon_index] + id.point_index;
|
||||
assert(res < m_count);
|
||||
return res;
|
||||
}
|
||||
|
||||
ExPolygonsIndex ExPolygonsIndices::cvt(uint32_t index) const
|
||||
{
|
||||
assert(index < m_count);
|
||||
ExPolygonsIndex result{0, 0, 0};
|
||||
// find expolygon index
|
||||
auto fn = [](const std::vector<uint32_t> &offsets, uint32_t index) { return offsets[0] < index; };
|
||||
auto it = std::lower_bound(m_offsets.begin() + 1, m_offsets.end(), index, fn);
|
||||
result.expolygons_index = it - m_offsets.begin();
|
||||
if (it == m_offsets.end() || it->at(0) != index) --result.expolygons_index;
|
||||
|
||||
// find polygon index
|
||||
const std::vector<uint32_t> &shape_offset = m_offsets[result.expolygons_index];
|
||||
auto it2 = std::lower_bound(shape_offset.begin() + 1, shape_offset.end(), index);
|
||||
result.polygon_index = it2 - shape_offset.begin();
|
||||
if (it2 == shape_offset.end() || *it2 != index) --result.polygon_index;
|
||||
|
||||
// calculate point index
|
||||
uint32_t polygon_offset = shape_offset[result.polygon_index];
|
||||
assert(index >= polygon_offset);
|
||||
result.point_index = index - polygon_offset;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ExPolygonsIndices::is_last_point(const ExPolygonsIndex &id) const {
|
||||
assert(id.expolygons_index < m_offsets.size());
|
||||
const std::vector<uint32_t> &shape_offset = m_offsets[id.expolygons_index];
|
||||
assert(id.polygon_index < shape_offset.size());
|
||||
uint32_t index = shape_offset[id.polygon_index] + id.point_index;
|
||||
assert(index < m_count);
|
||||
// next index
|
||||
uint32_t next_point_index = index + 1;
|
||||
uint32_t next_poly_index = id.polygon_index + 1;
|
||||
uint32_t next_expoly_index = id.expolygons_index + 1;
|
||||
// is last expoly?
|
||||
if (next_expoly_index == m_offsets.size()) {
|
||||
// is last expoly last poly?
|
||||
if (next_poly_index == shape_offset.size())
|
||||
return next_point_index == m_count;
|
||||
} else {
|
||||
// (not last expoly) is expoly last poly?
|
||||
if (next_poly_index == shape_offset.size())
|
||||
return next_point_index == m_offsets[next_expoly_index][0];
|
||||
}
|
||||
// Not last polygon in expolygon
|
||||
return next_point_index == shape_offset[next_poly_index];
|
||||
}
|
||||
|
||||
uint32_t ExPolygonsIndices::get_count() const { return m_count; }
|
74
src/libslic3r/ExPolygonsIndex.hpp
Normal file
74
src/libslic3r/ExPolygonsIndex.hpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
#ifndef slic3r_ExPolygonsIndex_hpp_
|
||||
#define slic3r_ExPolygonsIndex_hpp_
|
||||
|
||||
#include "ExPolygon.hpp"
|
||||
namespace Slic3r {
|
||||
|
||||
/// <summary>
|
||||
/// Index into ExPolygons
|
||||
/// Identify expolygon, its contour (or hole) and point
|
||||
/// </summary>
|
||||
struct ExPolygonsIndex
|
||||
{
|
||||
// index of ExPolygons
|
||||
uint32_t expolygons_index;
|
||||
|
||||
// index of Polygon
|
||||
// 0 .. contour
|
||||
// N .. hole[N-1]
|
||||
uint32_t polygon_index;
|
||||
|
||||
// index of point in polygon
|
||||
uint32_t point_index;
|
||||
|
||||
bool is_contour() const { return polygon_index == 0; }
|
||||
bool is_hole() const { return polygon_index != 0; }
|
||||
uint32_t hole_index() const { return polygon_index - 1; }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Keep conversion from ExPolygonsIndex to Index and vice versa
|
||||
/// ExPolygonsIndex .. contour(or hole) point from ExPolygons
|
||||
/// Index .. continous number
|
||||
///
|
||||
/// index is used to address lines and points as result from function
|
||||
/// Slic3r::to_lines, Slic3r::to_points
|
||||
/// </summary>
|
||||
class ExPolygonsIndices
|
||||
{
|
||||
std::vector<std::vector<uint32_t>> m_offsets;
|
||||
// for check range of index
|
||||
uint32_t m_count; // count of points
|
||||
public:
|
||||
ExPolygonsIndices(const ExPolygons &shapes);
|
||||
|
||||
/// <summary>
|
||||
/// Convert to one index number
|
||||
/// </summary>
|
||||
/// <param name="id">Compose of adress into expolygons</param>
|
||||
/// <returns>Index</returns>
|
||||
uint32_t cvt(const ExPolygonsIndex &id) const;
|
||||
|
||||
/// <summary>
|
||||
/// Separate to multi index
|
||||
/// </summary>
|
||||
/// <param name="index">adress into expolygons</param>
|
||||
/// <returns></returns>
|
||||
ExPolygonsIndex cvt(uint32_t index) const;
|
||||
|
||||
/// <summary>
|
||||
/// Check whether id is last point in polygon
|
||||
/// </summary>
|
||||
/// <param name="id">Identify point in expolygon</param>
|
||||
/// <returns>True when id is last point in polygon otherwise false</returns>
|
||||
bool is_last_point(const ExPolygonsIndex &id) const;
|
||||
|
||||
/// <summary>
|
||||
/// Count of points in expolygons
|
||||
/// </summary>
|
||||
/// <returns>Count of points in expolygons</returns>
|
||||
uint32_t get_count() const;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
#endif // slic3r_ExPolygonsIndex_hpp_
|
|
@ -76,7 +76,11 @@ public:
|
|||
if (entities.empty())
|
||||
entities = std::move(src);
|
||||
else {
|
||||
std::move(std::begin(src), std::end(src), std::back_inserter(entities));
|
||||
entities.insert(entities.end(),
|
||||
std::make_move_iterator(src.begin()),
|
||||
std::make_move_iterator(src.end()));
|
||||
// Removing pointers to polymorphic extrusions from the donor object
|
||||
// so that they will not be deleted twice.
|
||||
src.clear();
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
170
src/libslic3r/IntersectionPoints.cpp
Normal file
170
src/libslic3r/IntersectionPoints.cpp
Normal file
|
@ -0,0 +1,170 @@
|
|||
#include "IntersectionPoints.hpp"
|
||||
|
||||
//#define USE_CGAL_SWEEP_LINE
|
||||
#ifdef USE_CGAL_SWEEP_LINE
|
||||
|
||||
#include <CGAL/Cartesian.h>
|
||||
#include <CGAL/MP_Float.h>
|
||||
#include <CGAL/Quotient.h>
|
||||
#include <CGAL/Arr_segment_traits_2.h>
|
||||
#include <CGAL/Sweep_line_2_algorithms.h>
|
||||
|
||||
using NT = CGAL::Quotient<CGAL::MP_Float>;
|
||||
using Kernel = CGAL::Cartesian<NT>;
|
||||
using P2 = Kernel::Point_2;
|
||||
using Traits_2 = CGAL::Arr_segment_traits_2<Kernel>;
|
||||
using Segment = Traits_2::Curve_2;
|
||||
using Segments = std::vector<Segment>;
|
||||
|
||||
namespace priv {
|
||||
|
||||
P2 convert(const Slic3r::Point &p) { return P2(p.x(), p.y()); }
|
||||
Slic3r::Vec2d convert(const P2 &p)
|
||||
{
|
||||
return Slic3r::Vec2d(CGAL::to_double(p.x()), CGAL::to_double(p.y()));
|
||||
}
|
||||
|
||||
Slic3r::Pointfs compute_intersections(const Segments &segments)
|
||||
{
|
||||
std::vector<P2> intersections;
|
||||
// Compute all intersection points.
|
||||
CGAL::compute_intersection_points(segments.begin(), segments.end(),
|
||||
std::back_inserter(intersections));
|
||||
if (intersections.empty()) return {};
|
||||
Slic3r::Pointfs pts;
|
||||
pts.reserve(intersections.size());
|
||||
for (const P2 &p : intersections) pts.push_back(convert(p));
|
||||
return pts;
|
||||
}
|
||||
|
||||
void add_polygon(const Slic3r::Polygon &polygon, Segments &segments)
|
||||
{
|
||||
if (polygon.points.size() < 2) return;
|
||||
P2 prev_point = priv::convert(polygon.last_point());
|
||||
for (const Slic3r::Point &p : polygon.points) {
|
||||
P2 act_point = priv::convert(p);
|
||||
if (prev_point == act_point) continue;
|
||||
segments.emplace_back(prev_point, act_point);
|
||||
prev_point = act_point;
|
||||
}
|
||||
}
|
||||
Slic3r::Pointfs Slic3r::intersection_points(const Lines &lines)
|
||||
{
|
||||
return priv::compute_intersections2(lines);
|
||||
Segments segments;
|
||||
segments.reserve(lines.size());
|
||||
for (Line l : lines)
|
||||
segments.emplace_back(priv::convert(l.a), priv::convert(l.b));
|
||||
return priv::compute_intersections(segments);
|
||||
}
|
||||
|
||||
Slic3r::Pointfs Slic3r::intersection_points(const Polygon &polygon)
|
||||
{
|
||||
Segments segments;
|
||||
segments.reserve(polygon.points.size());
|
||||
priv::add_polygon(polygon, segments);
|
||||
return priv::compute_intersections(segments);
|
||||
}
|
||||
|
||||
Slic3r::Pointfs Slic3r::intersection_points(const Polygons &polygons)
|
||||
{
|
||||
Segments segments;
|
||||
segments.reserve(count_points(polygons));
|
||||
for (const Polygon &polygon : polygons)
|
||||
priv::add_polygon(polygon, segments);
|
||||
return priv::compute_intersections(segments);
|
||||
}
|
||||
|
||||
Slic3r::Pointfs Slic3r::intersection_points(const ExPolygon &expolygon)
|
||||
{
|
||||
Segments segments;
|
||||
segments.reserve(count_points(expolygon));
|
||||
priv::add_polygon(expolygon.contour, segments);
|
||||
for (const Polygon &hole : expolygon.holes)
|
||||
priv::add_polygon(hole, segments);
|
||||
return priv::compute_intersections(segments);
|
||||
}
|
||||
|
||||
Slic3r::Pointfs Slic3r::intersection_points(const ExPolygons &expolygons)
|
||||
{
|
||||
Segments segments;
|
||||
segments.reserve(count_points(expolygons));
|
||||
for (const ExPolygon &expolygon : expolygons) {
|
||||
priv::add_polygon(expolygon.contour, segments);
|
||||
for (const Polygon &hole : expolygon.holes)
|
||||
priv::add_polygon(hole, segments);
|
||||
}
|
||||
return priv::compute_intersections(segments);
|
||||
}
|
||||
|
||||
|
||||
} // namespace priv
|
||||
|
||||
#else // USE_CGAL_SWEEP_LINE
|
||||
|
||||
// use bounding boxes
|
||||
#include <libslic3r/BoundingBox.hpp>
|
||||
|
||||
namespace priv {
|
||||
Slic3r::Pointfs compute_intersections(const Slic3r::Lines &lines)
|
||||
{
|
||||
using namespace Slic3r;
|
||||
// IMPROVE0: BoundingBoxes of Polygons
|
||||
// IMPROVE1: Polygon's neighbor lines can't intersect
|
||||
// e.g. use indices to Point to find same points
|
||||
// IMPROVE2: Use BentleyOttmann algorithm
|
||||
// https://doc.cgal.org/latest/Surface_sweep_2/index.html -- CGAL implementation is significantly slower
|
||||
// https://stackoverflow.com/questions/4407493/is-there-a-robust-c-implementation-of-the-bentley-ottmann-algorithm
|
||||
Pointfs pts;
|
||||
Point i;
|
||||
for (size_t li = 0; li < lines.size(); ++li) {
|
||||
const Line &l = lines[li];
|
||||
const Point &a = l.a;
|
||||
const Point &b = l.b;
|
||||
Point min(std::min(a.x(), b.x()), std::min(a.y(), b.y()));
|
||||
Point max(std::max(a.x(), b.x()), std::max(a.y(), b.y()));
|
||||
BoundingBox bb(min, max);
|
||||
for (size_t li_ = li + 1; li_ < lines.size(); ++li_) {
|
||||
const Line &l_ = lines[li_];
|
||||
const Point &a_ = l_.a;
|
||||
const Point &b_ = l_.b;
|
||||
if (a == b_ || b == a_ || a == a_ || b == b_) continue;
|
||||
Point min_(std::min(a_.x(), b_.x()), std::min(a_.y(), b_.y()));
|
||||
Point max_(std::max(a_.x(), b_.x()), std::max(a_.y(), b_.y()));
|
||||
BoundingBox bb_(min_, max_);
|
||||
// intersect of BB compare min max
|
||||
if (bb.intersects(bb_) &&
|
||||
l.intersection(l_, &i))
|
||||
pts.push_back(i.cast<double>());
|
||||
}
|
||||
}
|
||||
return pts;
|
||||
}
|
||||
} // namespace priv
|
||||
|
||||
Slic3r::Pointfs Slic3r::intersection_points(const Lines &lines)
|
||||
{
|
||||
return priv::compute_intersections(lines);
|
||||
}
|
||||
|
||||
Slic3r::Pointfs Slic3r::intersection_points(const Polygon &polygon)
|
||||
{
|
||||
return priv::compute_intersections(to_lines(polygon));
|
||||
}
|
||||
|
||||
Slic3r::Pointfs Slic3r::intersection_points(const Polygons &polygons)
|
||||
{
|
||||
return priv::compute_intersections(to_lines(polygons));
|
||||
}
|
||||
|
||||
Slic3r::Pointfs Slic3r::intersection_points(const ExPolygon &expolygon)
|
||||
{
|
||||
return priv::compute_intersections(to_lines(expolygon));
|
||||
}
|
||||
|
||||
Slic3r::Pointfs Slic3r::intersection_points(const ExPolygons &expolygons)
|
||||
{
|
||||
return priv::compute_intersections(to_lines(expolygons));
|
||||
}
|
||||
|
||||
#endif // USE_CGAL_SWEEP_LINE
|
16
src/libslic3r/IntersectionPoints.hpp
Normal file
16
src/libslic3r/IntersectionPoints.hpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef slic3r_IntersectionPoints_hpp_
|
||||
#define slic3r_IntersectionPoints_hpp_
|
||||
|
||||
#include "ExPolygon.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// collect all intersecting points
|
||||
Pointfs intersection_points(const Lines &lines);
|
||||
Pointfs intersection_points(const Polygon &polygon);
|
||||
Pointfs intersection_points(const Polygons &polygons);
|
||||
Pointfs intersection_points(const ExPolygon &expolygon);
|
||||
Pointfs intersection_points(const ExPolygons &expolygons);
|
||||
|
||||
} // namespace Slic3r
|
||||
#endif // slic3r_IntersectionPoints_hpp_
|
|
@ -210,6 +210,7 @@ public:
|
|||
static const constexpr int Dim = 2;
|
||||
using Scalar = Vec2d::Scalar;
|
||||
};
|
||||
using Linesf = std::vector<Linef>;
|
||||
|
||||
class Linef3
|
||||
{
|
||||
|
|
|
@ -14,12 +14,15 @@
|
|||
#include "Arrange.hpp"
|
||||
#include "CustomGCode.hpp"
|
||||
#include "enum_bitmask.hpp"
|
||||
//#include "ModelVolumeType.hpp"
|
||||
#include "TextConfiguration.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
namespace cereal {
|
||||
class BinaryInputArchive;
|
||||
|
@ -765,6 +768,7 @@ public:
|
|||
void set_mesh(std::shared_ptr<const TriangleMesh> &mesh) { m_mesh = mesh; }
|
||||
void set_mesh(std::unique_ptr<const TriangleMesh> &&mesh) { m_mesh = std::move(mesh); }
|
||||
void reset_mesh() { m_mesh = std::make_shared<const TriangleMesh>(); }
|
||||
const std::shared_ptr<const TriangleMesh>& get_mesh_shared_ptr() const { return m_mesh; }
|
||||
// Configuration parameters specific to an object model geometry or a modifier volume,
|
||||
// overriding the global Slic3r settings and the ModelObject settings.
|
||||
ModelConfigObject config;
|
||||
|
@ -778,6 +782,10 @@ public:
|
|||
// List of mesh facets painted for MMU segmentation.
|
||||
FacetsAnnotation mmu_segmentation_facets;
|
||||
|
||||
// Is set only when volume is Embossed Text type
|
||||
// Contain information how to re-create volume
|
||||
std::optional<TextConfiguration> text_configuration;
|
||||
|
||||
// A parent object owning this modifier volume.
|
||||
ModelObject* get_object() const { return this->object; }
|
||||
ModelVolumeType type() const { return m_type; }
|
||||
|
@ -928,23 +936,8 @@ private:
|
|||
// 1 -> is splittable
|
||||
mutable int m_is_splittable{ -1 };
|
||||
|
||||
ModelVolume(ModelObject *object, const TriangleMesh &mesh, ModelVolumeType type = ModelVolumeType::MODEL_PART) : m_mesh(new TriangleMesh(mesh)), m_type(type), object(object)
|
||||
{
|
||||
assert(this->id().valid());
|
||||
assert(this->config.id().valid());
|
||||
assert(this->supported_facets.id().valid());
|
||||
assert(this->seam_facets.id().valid());
|
||||
assert(this->mmu_segmentation_facets.id().valid());
|
||||
assert(this->id() != this->config.id());
|
||||
assert(this->id() != this->supported_facets.id());
|
||||
assert(this->id() != this->seam_facets.id());
|
||||
assert(this->id() != this->mmu_segmentation_facets.id());
|
||||
if (mesh.facets_count() > 1)
|
||||
calculate_convex_hull();
|
||||
}
|
||||
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull, ModelVolumeType type = ModelVolumeType::MODEL_PART) :
|
||||
m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(type), object(object) {
|
||||
assert(this->id().valid());
|
||||
inline bool check() {
|
||||
assert(this->id().valid());
|
||||
assert(this->config.id().valid());
|
||||
assert(this->supported_facets.id().valid());
|
||||
assert(this->seam_facets.id().valid());
|
||||
|
@ -953,6 +946,24 @@ private:
|
|||
assert(this->id() != this->supported_facets.id());
|
||||
assert(this->id() != this->seam_facets.id());
|
||||
assert(this->id() != this->mmu_segmentation_facets.id());
|
||||
return true;
|
||||
}
|
||||
|
||||
ModelVolume(ModelObject *object, const TriangleMesh &mesh, ModelVolumeType type = ModelVolumeType::MODEL_PART) :
|
||||
m_mesh(new TriangleMesh(mesh)), m_type(type), object(object)
|
||||
{
|
||||
assert(check());
|
||||
if (m_mesh->facets_count() > 1) calculate_convex_hull();
|
||||
}
|
||||
ModelVolume(ModelObject *object, TriangleMesh &&mesh, ModelVolumeType type = ModelVolumeType::MODEL_PART)
|
||||
: m_mesh(new TriangleMesh(std::move(mesh))), m_type(type), object(object)
|
||||
{
|
||||
assert(check());
|
||||
if (m_mesh->facets_count() > 1) calculate_convex_hull();
|
||||
}
|
||||
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull, ModelVolumeType type = ModelVolumeType::MODEL_PART) :
|
||||
m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(type), object(object) {
|
||||
assert(check());
|
||||
}
|
||||
|
||||
// Copying an existing volume, therefore this volume will get a copy of the ID assigned.
|
||||
|
@ -961,7 +972,8 @@ private:
|
|||
name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull),
|
||||
config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation),
|
||||
supported_facets(other.supported_facets), seam_facets(other.seam_facets), mmu_segmentation_facets(other.mmu_segmentation_facets),
|
||||
cut_info(other.cut_info)
|
||||
cut_info(other.cut_info),
|
||||
text_configuration(other.text_configuration)
|
||||
{
|
||||
assert(this->id().valid());
|
||||
assert(this->config.id().valid());
|
||||
|
@ -982,7 +994,8 @@ private:
|
|||
// Providing a new mesh, therefore this volume will get a new unique ID assigned.
|
||||
ModelVolume(ModelObject *object, const ModelVolume &other, TriangleMesh &&mesh) :
|
||||
name(other.name), source(other.source), config(other.config), object(object), m_mesh(new TriangleMesh(std::move(mesh))), m_type(other.m_type), m_transformation(other.m_transformation),
|
||||
cut_info(other.cut_info)
|
||||
cut_info(other.cut_info),
|
||||
text_configuration(other.text_configuration)
|
||||
{
|
||||
assert(this->id().valid());
|
||||
assert(this->config.id().valid());
|
||||
|
@ -1029,6 +1042,7 @@ private:
|
|||
cereal::load_by_value(ar, seam_facets);
|
||||
cereal::load_by_value(ar, mmu_segmentation_facets);
|
||||
cereal::load_by_value(ar, config);
|
||||
cereal::load(ar, text_configuration);
|
||||
assert(m_mesh);
|
||||
if (has_convex_hull) {
|
||||
cereal::load_optional(ar, m_convex_hull);
|
||||
|
@ -1045,6 +1059,7 @@ private:
|
|||
cereal::save_by_value(ar, seam_facets);
|
||||
cereal::save_by_value(ar, mmu_segmentation_facets);
|
||||
cereal::save_by_value(ar, config);
|
||||
cereal::save(ar, text_configuration);
|
||||
if (has_convex_hull)
|
||||
cereal::save_optional(ar, m_convex_hull);
|
||||
}
|
||||
|
|
16
src/libslic3r/ModelVolumeType.hpp
Normal file
16
src/libslic3r/ModelVolumeType.hpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef slic3r_ModelVolumeType_hpp_
|
||||
#define slic3r_ModelVolumeType_hpp_
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
enum class ModelVolumeType : int {
|
||||
INVALID = -1,
|
||||
MODEL_PART = 0,
|
||||
NEGATIVE_VOLUME,
|
||||
PARAMETER_MODIFIER,
|
||||
SUPPORT_BLOCKER,
|
||||
SUPPORT_ENFORCER,
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
#endif /* slic3r_ModelVolumeType_hpp_ */
|
83
src/libslic3r/NSVGUtils.cpp
Normal file
83
src/libslic3r/NSVGUtils.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
#include "NSVGUtils.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
void NSVGUtils::flatten_cubic_bez(Polygon &polygon,
|
||||
float tessTol,
|
||||
Vec2f p1,
|
||||
Vec2f p2,
|
||||
Vec2f p3,
|
||||
Vec2f p4,
|
||||
int level)
|
||||
{
|
||||
Vec2f p12 = (p1 + p2) * 0.5f;
|
||||
Vec2f p23 = (p2 + p3) * 0.5f;
|
||||
Vec2f p34 = (p3 + p4) * 0.5f;
|
||||
Vec2f p123 = (p12 + p23) * 0.5f;
|
||||
|
||||
Vec2f pd = p4 - p1;
|
||||
Vec2f pd2 = p2 - p4;
|
||||
float d2 = std::abs(pd2.x() * pd.y() - pd2.y() * pd.x());
|
||||
Vec2f pd3 = p3 - p4;
|
||||
float d3 = std::abs(pd3.x() * pd.y() - pd3.y() * pd.x());
|
||||
float d23 = d2 + d3;
|
||||
|
||||
if ((d23 * d23) < tessTol * (pd.x() * pd.x() + pd.y() * pd.y())) {
|
||||
polygon.points.emplace_back(p4.x(), p4.y());
|
||||
return;
|
||||
}
|
||||
|
||||
--level;
|
||||
if (level == 0) return;
|
||||
Vec2f p234 = (p23 + p34) * 0.5f;
|
||||
Vec2f p1234 = (p123 + p234) * 0.5f;
|
||||
flatten_cubic_bez(polygon, tessTol, p1, p12, p123, p1234, level);
|
||||
flatten_cubic_bez(polygon, tessTol, p1234, p234, p34, p4, level);
|
||||
}
|
||||
|
||||
Polygons NSVGUtils::to_polygons(NSVGimage *image, float tessTol, int max_level)
|
||||
{
|
||||
Polygons polygons;
|
||||
for (NSVGshape *shape = image->shapes; shape != NULL;
|
||||
shape = shape->next) {
|
||||
if (!(shape->flags & NSVG_FLAGS_VISIBLE)) continue;
|
||||
Slic3r::Polygon polygon;
|
||||
if (shape->fill.type != NSVG_PAINT_NONE) {
|
||||
for (NSVGpath *path = shape->paths; path != NULL;
|
||||
path = path->next) {
|
||||
// Flatten path
|
||||
polygon.points.emplace_back(path->pts[0], path->pts[1]);
|
||||
size_t path_size = (path->npts > 1) ?
|
||||
static_cast<size_t>(path->npts - 1) : 0;
|
||||
for (size_t i = 0; i < path_size; i += 3) {
|
||||
float *p = &path->pts[i * 2];
|
||||
Vec2f p1(p[0], p[1]), p2(p[2], p[3]), p3(p[4], p[5]),
|
||||
p4(p[6], p[7]);
|
||||
flatten_cubic_bez(polygon, tessTol, p1, p2, p3, p4,
|
||||
max_level);
|
||||
}
|
||||
if (path->closed && !polygon.empty()) {
|
||||
polygons.push_back(polygon);
|
||||
polygon = Slic3r::Polygon();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!polygon.empty())
|
||||
polygons.push_back(polygon);
|
||||
}
|
||||
return polygons;
|
||||
}
|
||||
|
||||
ExPolygons NSVGUtils::to_ExPolygons(NSVGimage *image,
|
||||
float tessTol,
|
||||
int max_level)
|
||||
{
|
||||
Polygons polygons = to_polygons(image, tessTol, max_level);
|
||||
|
||||
// Fix Y axis
|
||||
for (Polygon &polygon : polygons)
|
||||
for (Point &p : polygon.points) p.y() *= -1;
|
||||
|
||||
return Slic3r::union_ex(polygons);
|
||||
}
|
34
src/libslic3r/NSVGUtils.hpp
Normal file
34
src/libslic3r/NSVGUtils.hpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#ifndef slic3r_NSVGUtils_hpp_
|
||||
#define slic3r_NSVGUtils_hpp_
|
||||
|
||||
#include "Polygon.hpp"
|
||||
#include "ExPolygon.hpp"
|
||||
#include "nanosvg/nanosvg.h" // load SVG file
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Helper function to work with nano svg
|
||||
class NSVGUtils
|
||||
{
|
||||
public:
|
||||
NSVGUtils() = delete;
|
||||
|
||||
// inspired by nanosvgrast.h function nsvgRasterize->nsvg__flattenShape
|
||||
static void flatten_cubic_bez(Polygon &polygon,
|
||||
float tessTol,
|
||||
Vec2f p1,
|
||||
Vec2f p2,
|
||||
Vec2f p3,
|
||||
Vec2f p4,
|
||||
int level);
|
||||
// convert svg image to ExPolygons
|
||||
static ExPolygons to_ExPolygons(NSVGimage *image,
|
||||
float tessTol = 10.,
|
||||
int max_level = 10);
|
||||
// convert svg paths to Polygons
|
||||
static Polygons to_polygons(NSVGimage *image,
|
||||
float tessTol = 10.,
|
||||
int max_level = 10);
|
||||
};
|
||||
} // namespace Slic3r
|
||||
#endif // slic3r_NSVGUtils_hpp_
|
|
@ -66,6 +66,24 @@ bool has_duplicate_points(std::vector<Point> &&pts)
|
|||
return false;
|
||||
}
|
||||
|
||||
Points collect_duplications(Points pts /* Copy */)
|
||||
{
|
||||
std::stable_sort(pts.begin(), pts.end());
|
||||
Points duplicits;
|
||||
const Point *prev = &pts.front();
|
||||
for (size_t i = 1; i < pts.size(); ++i) {
|
||||
const Point *act = &pts[i];
|
||||
if (*prev == *act) {
|
||||
// duplicit point
|
||||
if (!duplicits.empty() && duplicits.back() == *act)
|
||||
continue; // only unique duplicits
|
||||
duplicits.push_back(*act);
|
||||
}
|
||||
prev = act;
|
||||
}
|
||||
return duplicits;
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const Points &pts)
|
||||
{
|
||||
return BoundingBox(pts);
|
||||
|
|
|
@ -267,6 +267,9 @@ inline bool has_duplicate_successive_points_closed(const std::vector<Point> &pts
|
|||
return has_duplicate_successive_points(pts) || (pts.size() >= 2 && pts.front() == pts.back());
|
||||
}
|
||||
|
||||
// Collect adjecent(duplicit points)
|
||||
Points collect_duplications(Points pts /* Copy */);
|
||||
|
||||
inline bool shorter_then(const Point& p0, const coord_t len)
|
||||
{
|
||||
if (p0.x() > len || p0.x() < -len)
|
||||
|
@ -547,6 +550,7 @@ namespace boost { namespace polygon {
|
|||
} }
|
||||
// end Boost
|
||||
|
||||
#include <cereal/cereal.hpp>
|
||||
// Serialization through the Cereal library
|
||||
namespace cereal {
|
||||
// template<class Archive> void serialize(Archive& archive, Slic3r::Vec2crd &v) { archive(v.x(), v.y()); }
|
||||
|
@ -560,10 +564,11 @@ namespace cereal {
|
|||
template<class Archive> void serialize(Archive& archive, Slic3r::Vec2d &v) { archive(v.x(), v.y()); }
|
||||
template<class Archive> void serialize(Archive& archive, Slic3r::Vec3d &v) { archive(v.x(), v.y(), v.z()); }
|
||||
|
||||
template<class Archive> void load(Archive& archive, Slic3r::Matrix2f &m) { archive.loadBinary((char*)m.data(), sizeof(float) * 4); }
|
||||
template<class Archive> void save(Archive& archive, Slic3r::Matrix2f &m) { archive.saveBinary((char*)m.data(), sizeof(float) * 4); }
|
||||
template<class Archive> void load(Archive& archive, Slic3r::Transform3d& m) { archive.loadBinary((char*)m.data(), sizeof(double) * 16); }
|
||||
template<class Archive> void save(Archive& archive, const Slic3r::Transform3d& m) { archive.saveBinary((char*)m.data(), sizeof(double) * 16); }
|
||||
template<class Archive> void serialize(Archive& archive, Slic3r::Matrix4d &m){ archive(binary_data(m.data(), 4*4*sizeof(double))); }
|
||||
template<class Archive> void serialize(Archive& archive, Slic3r::Matrix2f &m){ archive(binary_data(m.data(), 2*2*sizeof(float))); }
|
||||
|
||||
// Eigen Transformation serialization
|
||||
template<class Archive, class T, int N> inline void serialize(Archive& archive, Eigen::Transform<T, N, Eigen::Affine, Eigen::DontAlign>& t){ archive(t.matrix()); }
|
||||
}
|
||||
|
||||
// To be able to use Vec<> and Mat<> in range based for loops:
|
||||
|
|
|
@ -169,13 +169,16 @@ inline Points to_points(const Polygon &poly)
|
|||
return poly.points;
|
||||
}
|
||||
|
||||
inline size_t count_points(const Polygons &polys) {
|
||||
size_t n_points = 0;
|
||||
for (const auto &poly: polys) n_points += poly.points.size();
|
||||
return n_points;
|
||||
}
|
||||
|
||||
inline Points to_points(const Polygons &polys)
|
||||
{
|
||||
size_t n_points = 0;
|
||||
for (size_t i = 0; i < polys.size(); ++ i)
|
||||
n_points += polys[i].points.size();
|
||||
Points points;
|
||||
points.reserve(n_points);
|
||||
points.reserve(count_points(polys));
|
||||
for (const Polygon &poly : polys)
|
||||
append(points, poly.points);
|
||||
return points;
|
||||
|
@ -195,11 +198,8 @@ inline Lines to_lines(const Polygon &poly)
|
|||
|
||||
inline Lines to_lines(const Polygons &polys)
|
||||
{
|
||||
size_t n_lines = 0;
|
||||
for (size_t i = 0; i < polys.size(); ++ i)
|
||||
n_lines += polys[i].points.size();
|
||||
Lines lines;
|
||||
lines.reserve(n_lines);
|
||||
lines.reserve(count_points(polys));
|
||||
for (size_t i = 0; i < polys.size(); ++ i) {
|
||||
const Polygon &poly = polys[i];
|
||||
for (Points::const_iterator it = poly.points.begin(); it != poly.points.end()-1; ++it)
|
||||
|
|
|
@ -184,7 +184,7 @@ void Slic3r::its_quadric_edge_collapse(
|
|||
throw_on_cancel();
|
||||
status_fn(status_init_size);
|
||||
|
||||
//its_store_triangle(its, "triangle.obj", 1182);
|
||||
//its_store_triangle_to_obj(its, "triangle.obj", 1182);
|
||||
//store_surround("triangle_surround1.obj", 1182, 1, its, v_infos, e_infos);
|
||||
|
||||
// convert from triangle index to mutable priority queue index
|
||||
|
@ -904,7 +904,7 @@ void QuadricEdgeCollapse::store_surround(const char *obj_filename,
|
|||
std::vector<size_t> trs;
|
||||
trs.reserve(triangles.size());
|
||||
for (size_t ti : triangles) trs.push_back(ti);
|
||||
its_store_triangles(its, obj_filename, trs);
|
||||
its_store_triangles_to_obj(its, obj_filename, trs);
|
||||
// its_write_obj(its,"original.obj");
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#ifndef slic3r_quadric_edge_collapse_hpp_
|
||||
#define slic3r_quadric_edge_collapse_hpp_
|
||||
|
||||
// paper: https://people.eecs.berkeley.edu/~jrs/meshpapers/GarlandHeckbert2.pdf
|
||||
// sum up: https://users.csc.calpoly.edu/~zwood/teaching/csc570/final06/jseeba/
|
||||
// inspiration: https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification
|
||||
|
@ -26,3 +29,4 @@ void its_quadric_edge_collapse(
|
|||
std::function<void(int)> statusfn = nullptr);
|
||||
|
||||
} // namespace Slic3r
|
||||
#endif // slic3r_quadric_edge_collapse_hpp_
|
||||
|
|
|
@ -378,4 +378,10 @@ void SVG::export_expolygons(const char *path, const std::vector<std::pair<Slic3r
|
|||
svg.Close();
|
||||
}
|
||||
|
||||
float SVG::to_svg_coord(float x) throw()
|
||||
{
|
||||
// return x;
|
||||
return unscale<float>(x) * 10.f;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -167,9 +167,9 @@ public:
|
|||
{ export_expolygons(path.c_str(), expolygons_with_attributes); }
|
||||
|
||||
private:
|
||||
static float to_svg_coord(float x) throw() { return unscale<float>(x) * 10.f; }
|
||||
static float to_svg_x(float x) throw() { return to_svg_coord(x); }
|
||||
float to_svg_y(float x) const throw() { return flipY ? this->height - to_svg_coord(x) : to_svg_coord(x); }
|
||||
static float to_svg_coord(float x) throw();
|
||||
static float to_svg_x(float x) throw() { return to_svg_coord(x); }
|
||||
float to_svg_y(float x) const throw() { return flipY ? this->height - to_svg_coord(x) : to_svg_coord(x); }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,69 +1,69 @@
|
|||
#ifndef _prusaslicer_technologies_h_
|
||||
#define _prusaslicer_technologies_h_
|
||||
|
||||
//=============
|
||||
// debug techs
|
||||
//=============
|
||||
// Shows camera target in the 3D scene
|
||||
#define ENABLE_SHOW_CAMERA_TARGET 0
|
||||
// Log debug messages to console when changing selection
|
||||
#define ENABLE_SELECTION_DEBUG_OUTPUT 0
|
||||
// Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active
|
||||
#define ENABLE_RENDER_SELECTION_CENTER 0
|
||||
// Shows an imgui dialog with camera related data
|
||||
#define ENABLE_CAMERA_STATISTICS 0
|
||||
// Enable extracting thumbnails from selected gcode and save them as png files
|
||||
#define ENABLE_THUMBNAIL_GENERATOR_DEBUG 0
|
||||
// Disable synchronization of unselected instances
|
||||
#define DISABLE_INSTANCES_SYNCH 0
|
||||
// Use wxDataViewRender instead of wxDataViewCustomRenderer
|
||||
#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING 0
|
||||
// Enable G-Code viewer statistics imgui dialog
|
||||
#define ENABLE_GCODE_VIEWER_STATISTICS 0
|
||||
// Enable G-Code viewer comparison between toolpaths height and width detected from gcode and calculated at gcode generation
|
||||
#define ENABLE_GCODE_VIEWER_DATA_CHECKING 0
|
||||
// Enable project dirty state manager debug window
|
||||
#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW 0
|
||||
// Disable using instanced models to render options in gcode preview
|
||||
#define DISABLE_GCODEVIEWER_INSTANCED_MODELS 1
|
||||
// Enable Measure Gizmo debug window
|
||||
#define ENABLE_MEASURE_GIZMO_DEBUG 0
|
||||
|
||||
|
||||
// Enable rendering of objects using environment map
|
||||
#define ENABLE_ENVIRONMENT_MAP 0
|
||||
// Enable smoothing of objects normals
|
||||
#define ENABLE_SMOOTH_NORMALS 0
|
||||
|
||||
|
||||
//====================
|
||||
// 2.5.0.alpha1 techs
|
||||
//====================
|
||||
#define ENABLE_2_5_0_ALPHA1 1
|
||||
|
||||
// Enable removal of legacy OpenGL calls
|
||||
#define ENABLE_LEGACY_OPENGL_REMOVAL (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable OpenGL ES
|
||||
#define ENABLE_OPENGL_ES (0 && ENABLE_LEGACY_OPENGL_REMOVAL)
|
||||
// Enable OpenGL core profile context (tested against Mesa 20.1.8 on Windows)
|
||||
#define ENABLE_GL_CORE_PROFILE (1 && ENABLE_LEGACY_OPENGL_REMOVAL && !ENABLE_OPENGL_ES)
|
||||
// Enable OpenGL debug messages using debug context
|
||||
#define ENABLE_OPENGL_DEBUG_OPTION (1 && ENABLE_GL_CORE_PROFILE)
|
||||
// Shows an imgui dialog with GLModel statistics data
|
||||
#define ENABLE_GLMODEL_STATISTICS (0 && ENABLE_LEGACY_OPENGL_REMOVAL)
|
||||
// Enable rework of Reload from disk command
|
||||
#define ENABLE_RELOAD_FROM_DISK_REWORK (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable editing volumes transformation in world coordinates and instances in local coordinates
|
||||
#define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable alternative version of file_wildcards()
|
||||
#define ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable processing of gcode G2 and G3 lines
|
||||
#define ENABLE_PROCESS_G2_G3_LINES (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable fix of used filament data exported to gcode file
|
||||
#define ENABLE_USED_FILAMENT_POST_PROCESS (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable picking using raytracing
|
||||
#define ENABLE_RAYCAST_PICKING (1 && ENABLE_LEGACY_OPENGL_REMOVAL)
|
||||
#define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING)
|
||||
|
||||
|
||||
#endif // _prusaslicer_technologies_h_
|
||||
#ifndef _prusaslicer_technologies_h_
|
||||
#define _prusaslicer_technologies_h_
|
||||
|
||||
//=============
|
||||
// debug techs
|
||||
//=============
|
||||
// Shows camera target in the 3D scene
|
||||
#define ENABLE_SHOW_CAMERA_TARGET 0
|
||||
// Log debug messages to console when changing selection
|
||||
#define ENABLE_SELECTION_DEBUG_OUTPUT 0
|
||||
// Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active
|
||||
#define ENABLE_RENDER_SELECTION_CENTER 0
|
||||
// Shows an imgui dialog with camera related data
|
||||
#define ENABLE_CAMERA_STATISTICS 0
|
||||
// Enable extracting thumbnails from selected gcode and save them as png files
|
||||
#define ENABLE_THUMBNAIL_GENERATOR_DEBUG 0
|
||||
// Disable synchronization of unselected instances
|
||||
#define DISABLE_INSTANCES_SYNCH 0
|
||||
// Use wxDataViewRender instead of wxDataViewCustomRenderer
|
||||
#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING 0
|
||||
// Enable G-Code viewer statistics imgui dialog
|
||||
#define ENABLE_GCODE_VIEWER_STATISTICS 0
|
||||
// Enable G-Code viewer comparison between toolpaths height and width detected from gcode and calculated at gcode generation
|
||||
#define ENABLE_GCODE_VIEWER_DATA_CHECKING 0
|
||||
// Enable project dirty state manager debug window
|
||||
#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW 0
|
||||
// Disable using instanced models to render options in gcode preview
|
||||
#define DISABLE_GCODEVIEWER_INSTANCED_MODELS 1
|
||||
// Enable Measure Gizmo debug window
|
||||
#define ENABLE_MEASURE_GIZMO_DEBUG 0
|
||||
|
||||
|
||||
// Enable rendering of objects using environment map
|
||||
#define ENABLE_ENVIRONMENT_MAP 0
|
||||
// Enable smoothing of objects normals
|
||||
#define ENABLE_SMOOTH_NORMALS 0
|
||||
|
||||
|
||||
//====================
|
||||
// 2.5.0.alpha1 techs
|
||||
//====================
|
||||
#define ENABLE_2_5_0_ALPHA1 1
|
||||
|
||||
// Enable removal of legacy OpenGL calls
|
||||
#define ENABLE_LEGACY_OPENGL_REMOVAL (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable OpenGL ES
|
||||
#define ENABLE_OPENGL_ES (0 && ENABLE_LEGACY_OPENGL_REMOVAL)
|
||||
// Enable OpenGL core profile context (tested against Mesa 20.1.8 on Windows)
|
||||
#define ENABLE_GL_CORE_PROFILE (1 && ENABLE_LEGACY_OPENGL_REMOVAL && !ENABLE_OPENGL_ES)
|
||||
// Enable OpenGL debug messages using debug context
|
||||
#define ENABLE_OPENGL_DEBUG_OPTION (1 && ENABLE_GL_CORE_PROFILE)
|
||||
// Shows an imgui dialog with GLModel statistics data
|
||||
#define ENABLE_GLMODEL_STATISTICS (0 && ENABLE_LEGACY_OPENGL_REMOVAL)
|
||||
// Enable rework of Reload from disk command
|
||||
#define ENABLE_RELOAD_FROM_DISK_REWORK (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable editing volumes transformation in world coordinates and instances in local coordinates
|
||||
#define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable alternative version of file_wildcards()
|
||||
#define ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable processing of gcode G2 and G3 lines
|
||||
#define ENABLE_PROCESS_G2_G3_LINES (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable fix of used filament data exported to gcode file
|
||||
#define ENABLE_USED_FILAMENT_POST_PROCESS (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable picking using raytracing
|
||||
#define ENABLE_RAYCAST_PICKING (1 && ENABLE_LEGACY_OPENGL_REMOVAL)
|
||||
#define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING)
|
||||
|
||||
|
||||
#endif // _prusaslicer_technologies_h_
|
||||
|
|
239
src/libslic3r/TextConfiguration.hpp
Normal file
239
src/libslic3r/TextConfiguration.hpp
Normal file
|
@ -0,0 +1,239 @@
|
|||
#ifndef slic3r_TextConfiguration_hpp_
|
||||
#define slic3r_TextConfiguration_hpp_
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <cereal/cereal.hpp>
|
||||
#include <cereal/types/optional.hpp>
|
||||
#include <cereal/types/string.hpp>
|
||||
#include <cereal/archives/binary.hpp>
|
||||
#include "Point.hpp" // Transform3d
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
/// <summary>
|
||||
/// User modifiable property of text style
|
||||
/// NOTE: OnEdit fix serializations: EmbossStylesSerializable, TextConfigurationSerialization
|
||||
/// </summary>
|
||||
struct FontProp
|
||||
{
|
||||
// define extra space between letters, negative mean closer letter
|
||||
// When not set value is zero and is not stored
|
||||
std::optional<int> char_gap; // [in font point]
|
||||
|
||||
// define extra space between lines, negative mean closer lines
|
||||
// When not set value is zero and is not stored
|
||||
std::optional<int> line_gap; // [in font point]
|
||||
|
||||
// Z depth of text
|
||||
float emboss; // [in mm]
|
||||
|
||||
// Flag that text should use surface cutted from object
|
||||
// FontProp::distance should without value
|
||||
// FontProp::emboss should be positive number
|
||||
// Note: default value is false
|
||||
bool use_surface;
|
||||
|
||||
// positive value mean wider character shape
|
||||
// negative value mean tiner character shape
|
||||
// When not set value is zero and is not stored
|
||||
std::optional<float> boldness; // [in mm]
|
||||
|
||||
// positive value mean italic of character (CW)
|
||||
// negative value mean CCW skew (unItalic)
|
||||
// When not set value is zero and is not stored
|
||||
std::optional<float> skew; // [ration x:y]
|
||||
|
||||
// distance from surface point
|
||||
// used for move over model surface
|
||||
// When not set value is zero and is not stored
|
||||
std::optional<float> distance; // [in mm]
|
||||
|
||||
// change up vector direction of font
|
||||
// When not set value is zero and is not stored
|
||||
std::optional<float> angle; // [in radians]
|
||||
|
||||
// Parameter for True Type Font collections
|
||||
// Select index of font in collection
|
||||
std::optional<unsigned int> collection_number;
|
||||
|
||||
//enum class Align {
|
||||
// left,
|
||||
// right,
|
||||
// center,
|
||||
// top_left,
|
||||
// top_right,
|
||||
// top_center,
|
||||
// bottom_left,
|
||||
// bottom_right,
|
||||
// bottom_center
|
||||
//};
|
||||
//// change pivot of text
|
||||
//// When not set, center is used and is not stored
|
||||
//std::optional<Align> align;
|
||||
|
||||
//////
|
||||
// Duplicit data to wxFontDescriptor
|
||||
// used for store/load .3mf file
|
||||
//////
|
||||
|
||||
// Height of text line (letters)
|
||||
// duplicit to wxFont::PointSize
|
||||
float size_in_mm; // [in mm]
|
||||
|
||||
// Additional data about font to be able to find substitution,
|
||||
// when same font is not installed
|
||||
std::optional<std::string> family;
|
||||
std::optional<std::string> face_name;
|
||||
std::optional<std::string> style;
|
||||
std::optional<std::string> weight;
|
||||
|
||||
/// <summary>
|
||||
/// Only constructor with restricted values
|
||||
/// </summary>
|
||||
/// <param name="line_height">Y size of text [in mm]</param>
|
||||
/// <param name="depth">Z size of text [in mm]</param>
|
||||
FontProp(float line_height = 10.f, float depth = 2.f)
|
||||
: emboss(depth), size_in_mm(line_height), use_surface(false)
|
||||
{}
|
||||
|
||||
bool operator==(const FontProp& other) const {
|
||||
return
|
||||
char_gap == other.char_gap &&
|
||||
line_gap == other.line_gap &&
|
||||
use_surface == other.use_surface &&
|
||||
is_approx(emboss, other.emboss) &&
|
||||
is_approx(size_in_mm, other.size_in_mm) &&
|
||||
is_approx(boldness, other.boldness) &&
|
||||
is_approx(skew, other.skew) &&
|
||||
is_approx(distance, other.distance) &&
|
||||
is_approx(angle, other.angle);
|
||||
}
|
||||
|
||||
// undo / redo stack recovery
|
||||
template<class Archive> void save(Archive &ar) const
|
||||
{
|
||||
ar(emboss, use_surface, size_in_mm);
|
||||
cereal::save(ar, char_gap);
|
||||
cereal::save(ar, line_gap);
|
||||
cereal::save(ar, boldness);
|
||||
cereal::save(ar, skew);
|
||||
cereal::save(ar, distance);
|
||||
cereal::save(ar, angle);
|
||||
cereal::save(ar, collection_number);
|
||||
cereal::save(ar, family);
|
||||
cereal::save(ar, face_name);
|
||||
cereal::save(ar, style);
|
||||
cereal::save(ar, weight);
|
||||
}
|
||||
template<class Archive> void load(Archive &ar)
|
||||
{
|
||||
ar(emboss, use_surface, size_in_mm);
|
||||
cereal::load(ar, char_gap);
|
||||
cereal::load(ar, line_gap);
|
||||
cereal::load(ar, boldness);
|
||||
cereal::load(ar, skew);
|
||||
cereal::load(ar, distance);
|
||||
cereal::load(ar, angle);
|
||||
cereal::load(ar, collection_number);
|
||||
cereal::load(ar, family);
|
||||
cereal::load(ar, face_name);
|
||||
cereal::load(ar, style);
|
||||
cereal::load(ar, weight);
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Style of embossed text
|
||||
/// (Path + Type) must define how to open font for using on different OS
|
||||
/// NOTE: OnEdit fix serializations: EmbossStylesSerializable, TextConfigurationSerialization
|
||||
/// </summary>
|
||||
struct EmbossStyle
|
||||
{
|
||||
// Human readable name of style it is shown in GUI
|
||||
std::string name;
|
||||
|
||||
// Define how to open font
|
||||
// Meaning depend on type
|
||||
std::string path;
|
||||
|
||||
enum class Type;
|
||||
// Define what is stored in path
|
||||
Type type { Type::undefined };
|
||||
|
||||
// User modification of font style
|
||||
FontProp prop;
|
||||
|
||||
// when name is empty than Font item was loaded from .3mf file
|
||||
// and potentionaly it is not reproducable
|
||||
// define data stored in path
|
||||
// when wx change way of storing add new descriptor Type
|
||||
enum class Type {
|
||||
undefined = 0,
|
||||
|
||||
// wx font descriptors are platform dependent
|
||||
// path is font descriptor generated by wxWidgets
|
||||
wx_win_font_descr, // on Windows
|
||||
wx_lin_font_descr, // on Linux
|
||||
wx_mac_font_descr, // on Max OS
|
||||
|
||||
// TrueTypeFont file loacation on computer
|
||||
// for privacy: only filename is stored into .3mf
|
||||
file_path
|
||||
};
|
||||
|
||||
bool operator==(const EmbossStyle &other) const
|
||||
{
|
||||
return
|
||||
type == other.type &&
|
||||
prop == other.prop &&
|
||||
name == other.name &&
|
||||
path == other.path
|
||||
;
|
||||
}
|
||||
|
||||
// undo / redo stack recovery
|
||||
template<class Archive> void serialize(Archive &ar){
|
||||
ar(name, path, type, prop);
|
||||
}
|
||||
};
|
||||
|
||||
// Emboss style name inside vector is unique
|
||||
// It is not map beacuse items has own order (view inside of slect)
|
||||
// It is stored into AppConfig by EmbossStylesSerializable
|
||||
using EmbossStyles = std::vector<EmbossStyle>;
|
||||
|
||||
/// <summary>
|
||||
/// Define how to create 'Text volume'
|
||||
/// It is stored into .3mf by TextConfigurationSerialization
|
||||
/// It is part of ModelVolume optional data
|
||||
/// </summary>
|
||||
struct TextConfiguration
|
||||
{
|
||||
// Style of embossed text
|
||||
EmbossStyle style;
|
||||
|
||||
// Embossed text value
|
||||
std::string text = "None";
|
||||
|
||||
// !!! Volume stored in .3mf has transformed vertices.
|
||||
// (baked transformation into vertices position)
|
||||
// Only place for fill this is when load from .3mf
|
||||
// This is correct volume transformation
|
||||
std::optional<Transform3d> fix_3mf_tr;
|
||||
|
||||
// undo / redo stack recovery
|
||||
template<class Archive> void save(Archive &ar) const{
|
||||
ar(text, style);
|
||||
cereal::save(ar, fix_3mf_tr);
|
||||
}
|
||||
template<class Archive> void load(Archive &ar){
|
||||
ar(text, style);
|
||||
cereal::load(ar, fix_3mf_tr);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_TextConfiguration_hpp_
|
12
src/libslic3r/Timer.cpp
Normal file
12
src/libslic3r/Timer.cpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include "Timer.hpp"
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
Slic3r::Timer::Timer(const std::string &name) : m_name(name), m_start(steady_clock::now()) {}
|
||||
|
||||
Slic3r::Timer::~Timer()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << "Timer '" << m_name << "' spend " <<
|
||||
duration_cast<milliseconds>(steady_clock::now() - m_start).count() << "ms";
|
||||
}
|
31
src/libslic3r/Timer.hpp
Normal file
31
src/libslic3r/Timer.hpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef libslic3r_Timer_hpp_
|
||||
#define libslic3r_Timer_hpp_
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
/// <summary>
|
||||
/// Instance of this class is used for measure time consumtion
|
||||
/// of block code until instance is alive and write result to debug output
|
||||
/// </summary>
|
||||
class Timer
|
||||
{
|
||||
std::string m_name;
|
||||
std::chrono::steady_clock::time_point m_start;
|
||||
public:
|
||||
/// <summary>
|
||||
/// name describe timer
|
||||
/// </summary>
|
||||
/// <param name="name">Describe timer in consol log</param>
|
||||
Timer(const std::string& name);
|
||||
|
||||
/// <summary>
|
||||
/// name describe timer
|
||||
/// </summary>
|
||||
~Timer();
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
#endif // libslic3r_Timer_hpp_
|
|
@ -783,9 +783,9 @@ int its_compactify_vertices(indexed_triangle_set &its, bool shrink_to_fit)
|
|||
return removed;
|
||||
}
|
||||
|
||||
bool its_store_triangle(const indexed_triangle_set &its,
|
||||
const char * obj_filename,
|
||||
size_t triangle_index)
|
||||
bool its_store_triangle_to_obj(const indexed_triangle_set &its,
|
||||
const char *obj_filename,
|
||||
size_t triangle_index)
|
||||
{
|
||||
if (its.indices.size() <= triangle_index) return false;
|
||||
Vec3i t = its.indices[triangle_index];
|
||||
|
@ -796,9 +796,9 @@ bool its_store_triangle(const indexed_triangle_set &its,
|
|||
return its_write_obj(its2, obj_filename);
|
||||
}
|
||||
|
||||
bool its_store_triangles(const indexed_triangle_set &its,
|
||||
const char * obj_filename,
|
||||
const std::vector<size_t> & triangles)
|
||||
bool its_store_triangles_to_obj(const indexed_triangle_set &its,
|
||||
const char *obj_filename,
|
||||
const std::vector<size_t> &triangles)
|
||||
{
|
||||
indexed_triangle_set its2;
|
||||
its2.vertices.reserve(triangles.size() * 3);
|
||||
|
@ -1206,6 +1206,23 @@ void its_reverse_all_facets(indexed_triangle_set &its)
|
|||
std::swap(face[0], face[1]);
|
||||
}
|
||||
|
||||
void its_merge(indexed_triangle_set &its, indexed_triangle_set &&its_add)
|
||||
{
|
||||
if (its.empty()) {
|
||||
its = std::move(its_add);
|
||||
return;
|
||||
}
|
||||
auto &verts = its.vertices;
|
||||
size_t verts_size = verts.size();
|
||||
Slic3r::append(verts, std::move(its_add.vertices));
|
||||
|
||||
// increase face indices
|
||||
int offset = static_cast<int>(verts_size);
|
||||
for (auto &face : its_add.indices)
|
||||
for (int i = 0; i < 3; ++i) face[i] += offset;
|
||||
Slic3r::append(its.indices, std::move(its_add.indices));
|
||||
}
|
||||
|
||||
void its_merge(indexed_triangle_set &A, const indexed_triangle_set &B)
|
||||
{
|
||||
auto N = int(A.vertices.size());
|
||||
|
|
|
@ -211,8 +211,8 @@ int its_remove_degenerate_faces(indexed_triangle_set &its, bool shrink_to_fit =
|
|||
int its_compactify_vertices(indexed_triangle_set &its, bool shrink_to_fit = true);
|
||||
|
||||
// store part of index triangle set
|
||||
bool its_store_triangle(const indexed_triangle_set &its, const char *obj_filename, size_t triangle_index);
|
||||
bool its_store_triangles(const indexed_triangle_set &its, const char *obj_filename, const std::vector<size_t>& triangles);
|
||||
bool its_store_triangle_to_obj(const indexed_triangle_set &its, const char *obj_filename, size_t triangle_index);
|
||||
bool its_store_triangles_to_obj(const indexed_triangle_set &its, const char *obj_filename, const std::vector<size_t>& triangles);
|
||||
|
||||
std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its);
|
||||
std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its, std::vector<Vec3i> &face_neighbors);
|
||||
|
@ -285,6 +285,14 @@ inline stl_normal its_unnormalized_normal(const indexed_triangle_set &its,
|
|||
float its_volume(const indexed_triangle_set &its);
|
||||
float its_average_edge_length(const indexed_triangle_set &its);
|
||||
|
||||
/// <summary>
|
||||
/// Merge one triangle mesh to another
|
||||
/// Added triangle set will be consumed
|
||||
/// </summary>
|
||||
/// <param name="its">IN/OUT triangle mesh</param>
|
||||
/// <param name="its_add">Triangle mesh (will be consumed)</param>
|
||||
void its_merge(indexed_triangle_set &its, indexed_triangle_set &&its_add);
|
||||
|
||||
void its_merge(indexed_triangle_set &A, const indexed_triangle_set &B);
|
||||
void its_merge(indexed_triangle_set &A, const std::vector<Vec3f> &triangles);
|
||||
void its_merge(indexed_triangle_set &A, const Pointf3s &triangles);
|
||||
|
|
328
src/libslic3r/Triangulation.cpp
Normal file
328
src/libslic3r/Triangulation.cpp
Normal file
|
@ -0,0 +1,328 @@
|
|||
#include "Triangulation.hpp"
|
||||
#include "IntersectionPoints.hpp"
|
||||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
#include <CGAL/Constrained_Delaunay_triangulation_2.h>
|
||||
#include <CGAL/Triangulation_vertex_base_with_info_2.h>
|
||||
#include <CGAL/spatial_sort.h>
|
||||
|
||||
using namespace Slic3r;
|
||||
namespace priv{
|
||||
inline void insert_edges(Triangulation::HalfEdges &edges, uint32_t &offset, const Polygon &polygon, const Triangulation::Changes& changes) {
|
||||
const Points &pts = polygon.points;
|
||||
uint32_t size = static_cast<uint32_t>(pts.size());
|
||||
uint32_t last_index = offset + size - 1;
|
||||
uint32_t prev_index = changes[last_index];
|
||||
for (uint32_t i = 0; i < size; ++i) {
|
||||
uint32_t index = changes[offset + i];
|
||||
// when duplicit points are neighbor
|
||||
if (prev_index == index) continue;
|
||||
edges.push_back({prev_index, index});
|
||||
prev_index = index;
|
||||
}
|
||||
offset += size;
|
||||
}
|
||||
|
||||
inline void insert_edges(Triangulation::HalfEdges &edges, uint32_t &offset, const Polygon &polygon) {
|
||||
const Points &pts = polygon.points;
|
||||
uint32_t size = static_cast<uint32_t>(pts.size());
|
||||
uint32_t prev_index = offset + size - 1;
|
||||
for (uint32_t i = 0; i < size; ++i) {
|
||||
uint32_t index = offset + i;
|
||||
edges.push_back({prev_index, index});
|
||||
prev_index = index;
|
||||
}
|
||||
offset += size;
|
||||
}
|
||||
|
||||
inline bool has_bidirectional_constrained(
|
||||
const Triangulation::HalfEdges &constrained)
|
||||
{
|
||||
for (const auto &c : constrained) {
|
||||
auto key = std::make_pair(c.second, c.first);
|
||||
auto it = std::lower_bound(constrained.begin(), constrained.end(),
|
||||
key);
|
||||
if (it != constrained.end() && *it == key) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool is_unique(const Points &points) {
|
||||
Points pts = points; // copy
|
||||
std::sort(pts.begin(), pts.end());
|
||||
auto it = std::adjacent_find(pts.begin(), pts.end());
|
||||
return it == pts.end();
|
||||
}
|
||||
|
||||
inline bool has_self_intersection(
|
||||
const Points &points,
|
||||
const Triangulation::HalfEdges &constrained_half_edges)
|
||||
{
|
||||
Lines lines;
|
||||
lines.reserve(constrained_half_edges.size());
|
||||
for (const auto &he : constrained_half_edges)
|
||||
lines.emplace_back(points[he.first], points[he.second]);
|
||||
return !intersection_points(lines).empty();
|
||||
}
|
||||
|
||||
} // namespace priv
|
||||
|
||||
//#define VISUALIZE_TRIANGULATION
|
||||
#ifdef VISUALIZE_TRIANGULATION
|
||||
#include "admesh/stl.h" // indexed triangle set
|
||||
static void visualize(const Points &points,
|
||||
const Triangulation::Indices &indices,
|
||||
const char *filename)
|
||||
{
|
||||
// visualize
|
||||
indexed_triangle_set its;
|
||||
its.vertices.reserve(points.size());
|
||||
for (const Point &p : points) its.vertices.emplace_back(p.x(), p.y(), 0.);
|
||||
its.indices = indices;
|
||||
its_write_obj(its, filename);
|
||||
}
|
||||
#endif // VISUALIZE_TRIANGULATION
|
||||
|
||||
Triangulation::Indices Triangulation::triangulate(const Points &points,
|
||||
const HalfEdges &constrained_half_edges)
|
||||
{
|
||||
assert(!points.empty());
|
||||
assert(!constrained_half_edges.empty());
|
||||
// constrained must be sorted
|
||||
assert(std::is_sorted(constrained_half_edges.begin(),
|
||||
constrained_half_edges.end()));
|
||||
// check that there is no duplicit constrained edge
|
||||
assert(std::adjacent_find(constrained_half_edges.begin(), constrained_half_edges.end()) == constrained_half_edges.end());
|
||||
// edges can NOT contain bidirectional constrained
|
||||
assert(!priv::has_bidirectional_constrained(constrained_half_edges));
|
||||
// check that there is only unique poistion of points
|
||||
assert(priv::is_unique(points));
|
||||
assert(!priv::has_self_intersection(points, constrained_half_edges));
|
||||
// use cgal triangulation
|
||||
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
|
||||
using Vb = CGAL::Triangulation_vertex_base_with_info_2<uint32_t, K>;
|
||||
using Fb = CGAL::Constrained_triangulation_face_base_2<K>;
|
||||
using Tds = CGAL::Triangulation_data_structure_2<Vb, Fb>;
|
||||
using CDT = CGAL::Constrained_Delaunay_triangulation_2<K, Tds, CGAL::Exact_predicates_tag>;
|
||||
|
||||
// construct a constrained triangulation
|
||||
CDT cdt;
|
||||
{
|
||||
std::vector<CDT::Vertex_handle> vertices_handle(points.size()); // for constriants
|
||||
using Point_with_ord = std::pair<CDT::Point, size_t>;
|
||||
using SearchTrait = CGAL::Spatial_sort_traits_adapter_2
|
||||
<K, CGAL::First_of_pair_property_map<Point_with_ord> >;
|
||||
|
||||
std::vector<Point_with_ord> cdt_points;
|
||||
cdt_points.reserve(points.size());
|
||||
size_t ord = 0;
|
||||
for (const auto &p : points)
|
||||
cdt_points.emplace_back(std::make_pair(CDT::Point{p.x(), p.y()}, ord++));
|
||||
|
||||
SearchTrait st;
|
||||
CGAL::spatial_sort(cdt_points.begin(), cdt_points.end(), st);
|
||||
CDT::Face_handle f;
|
||||
for (const auto& p : cdt_points) {
|
||||
auto handle = cdt.insert(p.first, f);
|
||||
handle->info() = p.second;
|
||||
vertices_handle[p.second] = handle;
|
||||
f = handle->face();
|
||||
}
|
||||
|
||||
// Constrain the triangulation.
|
||||
for (const HalfEdge &edge : constrained_half_edges)
|
||||
cdt.insert_constraint(vertices_handle[edge.first], vertices_handle[edge.second]);
|
||||
}
|
||||
|
||||
auto faces = cdt.finite_face_handles();
|
||||
|
||||
// Unmark constrained edges of outside faces.
|
||||
size_t num_faces = 0;
|
||||
for (CDT::Face_handle fh : faces) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (!fh->is_constrained(i)) continue;
|
||||
auto key = std::make_pair(fh->vertex((i + 2) % 3)->info(), fh->vertex((i + 1) % 3)->info());
|
||||
auto it = std::lower_bound(constrained_half_edges.begin(), constrained_half_edges.end(), key);
|
||||
if (it == constrained_half_edges.end() || *it != key) continue;
|
||||
// This face contains a constrained edge and it is outside.
|
||||
for (int j = 0; j < 3; ++ j)
|
||||
fh->set_constraint(j, false);
|
||||
--num_faces;
|
||||
break;
|
||||
}
|
||||
++num_faces;
|
||||
}
|
||||
|
||||
auto inside = [](CDT::Face_handle &fh) {
|
||||
return fh->neighbor(0) != fh &&
|
||||
(fh->is_constrained(0) ||
|
||||
fh->is_constrained(1) ||
|
||||
fh->is_constrained(2));
|
||||
};
|
||||
|
||||
#ifdef VISUALIZE_TRIANGULATION
|
||||
std::vector<Vec3i> indices2;
|
||||
indices2.reserve(num_faces);
|
||||
for (CDT::Face_handle fh : faces)
|
||||
if (inside(fh)) indices2.emplace_back(fh->vertex(0)->info(), fh->vertex(1)->info(), fh->vertex(2)->info());
|
||||
visualize(points, indices2, "C:/data/temp/triangulation_without_floodfill.obj");
|
||||
#endif // VISUALIZE_TRIANGULATION
|
||||
|
||||
// Propagate inside the constrained regions.
|
||||
std::vector<CDT::Face_handle> queue;
|
||||
queue.reserve(num_faces);
|
||||
for (CDT::Face_handle seed : faces){
|
||||
if (!inside(seed)) continue;
|
||||
// Seed fill to neighbor faces.
|
||||
queue.emplace_back(seed);
|
||||
while (! queue.empty()) {
|
||||
CDT::Face_handle fh = queue.back();
|
||||
queue.pop_back();
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (fh->is_constrained(i)) continue;
|
||||
// Propagate along this edge.
|
||||
fh->set_constraint(i, true);
|
||||
CDT::Face_handle nh = fh->neighbor(i);
|
||||
bool was_inside = inside(nh);
|
||||
// Mark the other side of this edge.
|
||||
nh->set_constraint(nh->index(fh), true);
|
||||
if (! was_inside)
|
||||
queue.push_back(nh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Vec3i> indices;
|
||||
indices.reserve(num_faces);
|
||||
for (CDT::Face_handle fh : faces)
|
||||
if (inside(fh))
|
||||
indices.emplace_back(fh->vertex(0)->info(), fh->vertex(1)->info(), fh->vertex(2)->info());
|
||||
|
||||
#ifdef VISUALIZE_TRIANGULATION
|
||||
visualize(points, indices, "C:/data/temp/triangulation.obj");
|
||||
#endif // VISUALIZE_TRIANGULATION
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
Triangulation::Indices Triangulation::triangulate(const Polygon &polygon)
|
||||
{
|
||||
const Points &pts = polygon.points;
|
||||
HalfEdges edges;
|
||||
edges.reserve(pts.size());
|
||||
uint32_t offset = 0;
|
||||
priv::insert_edges(edges, offset, polygon);
|
||||
std::sort(edges.begin(), edges.end());
|
||||
return triangulate(pts, edges);
|
||||
}
|
||||
|
||||
Triangulation::Indices Triangulation::triangulate(const Polygons &polygons)
|
||||
{
|
||||
size_t count = count_points(polygons);
|
||||
Points points;
|
||||
points.reserve(count);
|
||||
|
||||
HalfEdges edges;
|
||||
edges.reserve(count);
|
||||
uint32_t offset = 0;
|
||||
|
||||
for (const Polygon &polygon : polygons) {
|
||||
Slic3r::append(points, polygon.points);
|
||||
priv::insert_edges(edges, offset, polygon);
|
||||
}
|
||||
|
||||
std::sort(edges.begin(), edges.end());
|
||||
return triangulate(points, edges);
|
||||
}
|
||||
|
||||
Triangulation::Indices Triangulation::triangulate(const ExPolygon &expolygon){
|
||||
ExPolygons expolys({expolygon});
|
||||
return triangulate(expolys);
|
||||
}
|
||||
|
||||
Triangulation::Indices Triangulation::triangulate(const ExPolygons &expolygons){
|
||||
Points pts = to_points(expolygons);
|
||||
Points d_pts = collect_duplications(pts);
|
||||
if (d_pts.empty()) return triangulate(expolygons, pts);
|
||||
|
||||
Changes changes = create_changes(pts, d_pts);
|
||||
Indices indices = triangulate(expolygons, pts, changes);
|
||||
// reverse map for changes
|
||||
Changes changes2(changes.size(), std::numeric_limits<uint32_t>::max());
|
||||
for (size_t i = 0; i < changes.size(); ++i)
|
||||
changes2[changes[i]] = i;
|
||||
|
||||
// convert indices into expolygons indicies
|
||||
for (Vec3i &t : indices)
|
||||
for (size_t ti = 0; ti < 3; ti++) t[ti] = changes2[t[ti]];
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
Triangulation::Indices Triangulation::triangulate(const ExPolygons &expolygons, const Points &points)
|
||||
{
|
||||
assert(count_points(expolygons) == points.size());
|
||||
// when contain duplicit coordinate in points will not work properly
|
||||
assert(collect_duplications(points).empty());
|
||||
|
||||
HalfEdges edges;
|
||||
edges.reserve(points.size());
|
||||
uint32_t offset = 0;
|
||||
for (const ExPolygon &expolygon : expolygons) {
|
||||
priv::insert_edges(edges, offset, expolygon.contour);
|
||||
for (const Polygon &hole : expolygon.holes)
|
||||
priv::insert_edges(edges, offset, hole);
|
||||
}
|
||||
std::sort(edges.begin(), edges.end());
|
||||
return triangulate(points, edges);
|
||||
}
|
||||
|
||||
Triangulation::Indices Triangulation::triangulate(const ExPolygons &expolygons, const Points& points, const Changes& changes)
|
||||
{
|
||||
assert(!points.empty());
|
||||
assert(count_points(expolygons) == points.size());
|
||||
assert(changes.size() == points.size());
|
||||
// IMPROVE: search from end and somehow distiquish that value is not a change
|
||||
uint32_t count_points = *std::max_element(changes.begin(), changes.end())+1;
|
||||
Points pts(count_points);
|
||||
for (size_t i = 0; i < changes.size(); i++)
|
||||
pts[changes[i]] = points[i];
|
||||
|
||||
HalfEdges edges;
|
||||
edges.reserve(points.size());
|
||||
uint32_t offset = 0;
|
||||
for (const ExPolygon &expolygon : expolygons) {
|
||||
priv::insert_edges(edges, offset, expolygon.contour, changes);
|
||||
for (const Polygon &hole : expolygon.holes)
|
||||
priv::insert_edges(edges, offset, hole, changes);
|
||||
}
|
||||
|
||||
std::sort(edges.begin(), edges.end());
|
||||
return triangulate(pts, edges);
|
||||
}
|
||||
|
||||
Triangulation::Changes Triangulation::create_changes(const Points &points, const Points &duplicits)
|
||||
{
|
||||
assert(!duplicits.empty());
|
||||
assert(duplicits.size() < points.size()/2);
|
||||
std::vector<uint32_t> duplicit_indices(duplicits.size(), std::numeric_limits<uint32_t>::max());
|
||||
Changes changes;
|
||||
changes.reserve(points.size());
|
||||
uint32_t index = 0;
|
||||
for (const Point &p: points) {
|
||||
auto it = std::lower_bound(duplicits.begin(), duplicits.end(), p);
|
||||
if (it == duplicits.end() || *it != p) {
|
||||
changes.push_back(index);
|
||||
++index;
|
||||
continue;
|
||||
}
|
||||
uint32_t &d_index = duplicit_indices[it - duplicits.begin()];
|
||||
if (d_index == std::numeric_limits<uint32_t>::max()) {
|
||||
d_index = index;
|
||||
changes.push_back(index);
|
||||
++index;
|
||||
} else {
|
||||
changes.push_back(d_index);
|
||||
}
|
||||
}
|
||||
return changes;
|
||||
}
|
72
src/libslic3r/Triangulation.hpp
Normal file
72
src/libslic3r/Triangulation.hpp
Normal file
|
@ -0,0 +1,72 @@
|
|||
#ifndef libslic3r_Triangulation_hpp_
|
||||
#define libslic3r_Triangulation_hpp_
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <libslic3r/Point.hpp>
|
||||
#include <libslic3r/Polygon.hpp>
|
||||
#include <libslic3r/ExPolygon.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Triangulation
|
||||
{
|
||||
public:
|
||||
Triangulation() = delete;
|
||||
|
||||
// define oriented connection of 2 vertices(defined by its index)
|
||||
using HalfEdge = std::pair<uint32_t, uint32_t>;
|
||||
using HalfEdges = std::vector<HalfEdge>;
|
||||
using Indices = std::vector<Vec3i>;
|
||||
|
||||
/// <summary>
|
||||
/// Connect points by triangulation to create filled surface by triangles
|
||||
/// Input points have to be unique
|
||||
/// Inspiration for make unique points is Emboss::dilate_to_unique_points
|
||||
/// </summary>
|
||||
/// <param name="points">Points to connect</param>
|
||||
/// <param name="edges">Constraint for edges, pair is from point(first) to
|
||||
/// point(second), sorted lexicographically</param>
|
||||
/// <returns>Triangles</returns>
|
||||
static Indices triangulate(const Points &points,
|
||||
const HalfEdges &half_edges);
|
||||
static Indices triangulate(const Polygon &polygon);
|
||||
static Indices triangulate(const Polygons &polygons);
|
||||
static Indices triangulate(const ExPolygon &expolygon);
|
||||
static Indices triangulate(const ExPolygons &expolygons);
|
||||
|
||||
// Map for convert original index to set without duplication
|
||||
// from_index<to_index>
|
||||
using Changes = std::vector<uint32_t>;
|
||||
|
||||
/// <summary>
|
||||
/// Create conversion map from original index into new
|
||||
/// with respect of duplicit point
|
||||
/// </summary>
|
||||
/// <param name="points">input set of points</param>
|
||||
/// <param name="duplicits">duplicit points collected from points</param>
|
||||
/// <returns>Conversion map for point index</returns>
|
||||
static Changes create_changes(const Points &points, const Points &duplicits);
|
||||
|
||||
/// <summary>
|
||||
/// Triangulation for expolygons, speed up when points are already collected
|
||||
/// NOTE: Not working properly for ExPolygons with multiple point on same coordinate
|
||||
/// You should check it by "collect_changes"
|
||||
/// </summary>
|
||||
/// <param name="expolygons">Input shape to triangulation - define edges</param>
|
||||
/// <param name="points">Points from expolygons</param>
|
||||
/// <returns>Triangle indices</returns>
|
||||
static Indices triangulate(const ExPolygons &expolygons, const Points& points);
|
||||
|
||||
/// <summary>
|
||||
/// Triangulation for expolygons containing multiple points with same coordinate
|
||||
/// </summary>
|
||||
/// <param name="expolygons">Input shape to triangulation - define edge</param>
|
||||
/// <param name="points">Points from expolygons</param>
|
||||
/// <param name="changes">Changes swap for indicies into points</param>
|
||||
/// <returns>Triangle indices</returns>
|
||||
static Indices triangulate(const ExPolygons &expolygons, const Points& points, const Changes& changes);
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
#endif // libslic3r_Triangulation_hpp_
|
|
@ -237,6 +237,7 @@ inline typename CONTAINER_TYPE::value_type& next_value_modulo(typename CONTAINER
|
|||
}
|
||||
|
||||
extern std::string xml_escape(std::string text, bool is_marked = false);
|
||||
extern std::string xml_escape_double_quotes_attribute_value(std::string text);
|
||||
|
||||
|
||||
#if defined __GNUC__ && __GNUC__ < 5 && !defined __clang__
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
#include <optional>
|
||||
|
||||
#ifdef _WIN32
|
||||
// On MSVC, std::deque degenerates to a list of pointers, which defeats its purpose of reducing allocator load and memory fragmentation.
|
||||
|
@ -109,9 +110,10 @@ template <typename T>
|
|||
inline void append(std::vector<T>& dest, const std::vector<T>& src)
|
||||
{
|
||||
if (dest.empty())
|
||||
dest = src;
|
||||
dest = src; // copy
|
||||
else
|
||||
dest.insert(dest.end(), src.begin(), src.end());
|
||||
// NOTE: insert reserve space when needed
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
@ -120,11 +122,14 @@ inline void append(std::vector<T>& dest, std::vector<T>&& src)
|
|||
if (dest.empty())
|
||||
dest = std::move(src);
|
||||
else {
|
||||
dest.reserve(dest.size() + src.size());
|
||||
std::move(std::begin(src), std::end(src), std::back_inserter(dest));
|
||||
dest.insert(dest.end(),
|
||||
std::make_move_iterator(src.begin()),
|
||||
std::make_move_iterator(src.end()));
|
||||
|
||||
// Vojta wants back compatibility
|
||||
src.clear();
|
||||
src.shrink_to_fit();
|
||||
}
|
||||
src.clear();
|
||||
src.shrink_to_fit();
|
||||
}
|
||||
|
||||
template<class T, class... Args> // Arbitrary allocator can be used
|
||||
|
@ -140,8 +145,8 @@ void clear_and_shrink(std::vector<T, Args...>& vec)
|
|||
template <typename T>
|
||||
inline void append_reversed(std::vector<T>& dest, const std::vector<T>& src)
|
||||
{
|
||||
if (dest.empty())
|
||||
dest = src;
|
||||
if (dest.empty())
|
||||
dest = {src.rbegin(), src.rend()};
|
||||
else
|
||||
dest.insert(dest.end(), src.rbegin(), src.rend());
|
||||
}
|
||||
|
@ -151,11 +156,14 @@ template <typename T>
|
|||
inline void append_reversed(std::vector<T>& dest, std::vector<T>&& src)
|
||||
{
|
||||
if (dest.empty())
|
||||
dest = std::move(src);
|
||||
else {
|
||||
dest.reserve(dest.size() + src.size());
|
||||
std::move(std::rbegin(src), std::rend(src), std::back_inserter(dest));
|
||||
}
|
||||
dest = {std::make_move_iterator(src.rbegin),
|
||||
std::make_move_iterator(src.rend)};
|
||||
else
|
||||
dest.insert(dest.end(),
|
||||
std::make_move_iterator(src.rbegin()),
|
||||
std::make_move_iterator(src.rend()));
|
||||
|
||||
// Vojta wants back compatibility
|
||||
src.clear();
|
||||
src.shrink_to_fit();
|
||||
}
|
||||
|
@ -268,6 +276,14 @@ constexpr inline bool is_approx(Number value, Number test_value, Number precisio
|
|||
return std::fabs(double(value) - double(test_value)) < double(precision);
|
||||
}
|
||||
|
||||
template<typename Number>
|
||||
constexpr inline bool is_approx(const std::optional<Number> &value,
|
||||
const std::optional<Number> &test_value)
|
||||
{
|
||||
return (!value.has_value() && !test_value.has_value()) ||
|
||||
(value.has_value() && test_value.has_value() && is_approx<Number>(*value, *test_value));
|
||||
}
|
||||
|
||||
// A meta-predicate which is true for integers wider than or equal to coord_t
|
||||
template<class I> struct is_scaled_coord
|
||||
{
|
||||
|
|
|
@ -944,7 +944,34 @@ std::string xml_escape(std::string text, bool is_marked/* = false*/)
|
|||
case '\'': replacement = "'"; break;
|
||||
case '&': replacement = "&"; break;
|
||||
case '<': replacement = is_marked ? "<" :"<"; break;
|
||||
case '>': replacement = is_marked ? ">" :">"; break;
|
||||
case '>': replacement = is_marked ? ">" : ">"; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
text.replace(pos, 1, replacement);
|
||||
pos += replacement.size();
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
// Definition of escape symbols https://www.w3.org/TR/REC-xml/#AVNormalize
|
||||
|
||||
std::string xml_escape_double_quotes_attribute_value(std::string text)
|
||||
{
|
||||
std::string::size_type pos = 0;
|
||||
for (;;) {
|
||||
pos = text.find_first_of("\"&<\r\n\t", pos);
|
||||
if (pos == std::string::npos) break;
|
||||
|
||||
std::string replacement;
|
||||
switch (text[pos]) {
|
||||
case '\"': replacement = """; break;
|
||||
case '&': replacement = "&"; break;
|
||||
case '<': replacement = "<"; break;
|
||||
case '\r': replacement = "
"; break;
|
||||
case '\n': replacement = "
"; break;
|
||||
case '\t': replacement = "	"; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue