Better healing for Glyph shape - remove duplicit points + self intersections
Add search of intersecting points (compared with CGAL) Triangulation can [optionaly] accept multi points
This commit is contained in:
parent
94f735168c
commit
63121cee2e
18 changed files with 892 additions and 241 deletions
|
@ -172,6 +172,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);
|
||||
|
|
|
@ -388,9 +388,11 @@ cmake_policy(POP)
|
|||
|
||||
add_library(libslic3r_cgal STATIC
|
||||
CutSurface.hpp CutSurface.cpp
|
||||
IntersectionPoints.hpp IntersectionPoints.cpp
|
||||
MeshBoolean.hpp MeshBoolean.cpp
|
||||
TryCatchSignal.hpp TryCatchSignal.cpp
|
||||
Triangulation.hpp Triangulation.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
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <cstdlib>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
#include <ClipperUtils.hpp> // union_ex
|
||||
#include "IntersectionPoints.hpp"
|
||||
|
||||
#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
|
||||
#include "imgui/imstb_truetype.h" // stbtt_fontinfo
|
||||
|
@ -32,14 +33,6 @@ public:
|
|||
|
||||
static EmbossStyle create_style(std::wstring name, std::wstring path);
|
||||
|
||||
/// <summary>
|
||||
/// TODO: move to ExPolygon utils
|
||||
/// Remove multi points. When exist multi point dilate it by rect 3x3 and union result.
|
||||
/// </summary>
|
||||
/// <param name="expolygons">Shape which can contain same point, will be extended by dilatation rects</param>
|
||||
/// <returns>ExPolygons with only unique points</returns>
|
||||
static ExPolygons dilate_to_unique_points(ExPolygons &expolygons);
|
||||
|
||||
// scale and convert float to int coordinate
|
||||
static Point to_point(const stbtt__point &point);
|
||||
};
|
||||
|
@ -69,6 +62,112 @@ std::optional<stbtt_fontinfo> Private::load_font_info(
|
|||
return font_info;
|
||||
}
|
||||
|
||||
ExPolygons Emboss::heal_shape(const Polygons &shape) {
|
||||
// When edit this code check that font 'ALIENATE.TTF' and glyph 'i' still work
|
||||
// fix of self intersections
|
||||
// http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/SimplifyPolygon.htm
|
||||
ClipperLib::Paths paths = ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(shape));
|
||||
ClipperLib::CleanPolygons(paths);
|
||||
Polygons polygons = to_polygons(paths);
|
||||
static const Points pts_2x2({Point(0, 0), Point(1, 0), Point(1, 1), Point(0, 1)});
|
||||
static const Points pts_3x3({Point(-1, -1), Point(1, -1), Point(1, 1), Point(-1, 1)});
|
||||
|
||||
// do not remove all duplicits but do it better way
|
||||
Points duplicits = collect_duplications(to_points(polygons));
|
||||
if (!duplicits.empty()) {
|
||||
polygons.reserve(polygons.size() + duplicits.size());
|
||||
for (const Point &p : duplicits) {
|
||||
Slic3r::Polygon rect_3x3(pts_3x3);
|
||||
rect_3x3.translate(p);
|
||||
polygons.push_back(rect_3x3);
|
||||
}
|
||||
}
|
||||
ExPolygons res = Slic3r::union_ex(polygons, ClipperLib::pftNonZero);
|
||||
|
||||
Slic3r::Polygons holes;
|
||||
int max_iteration = 10;
|
||||
while (--max_iteration){
|
||||
Pointfs intersections = intersection_points(res);
|
||||
Points duplicits = collect_duplications(to_points(res));
|
||||
if (intersections.empty() && duplicits.empty()) break;
|
||||
|
||||
holes.clear();
|
||||
holes.reserve(intersections.size() + duplicits.size());
|
||||
|
||||
// Fix self intersection in result by subtracting hole 2x2
|
||||
if (!intersections.empty()) {
|
||||
for (const Vec2d &p : intersections) {
|
||||
Slic3r::Polygon hole(pts_2x2);
|
||||
Point tr(std::floor(p.x()), std::floor(p.y()));
|
||||
hole.translate(tr);
|
||||
holes.push_back(hole);
|
||||
}
|
||||
}
|
||||
|
||||
// fix duplicit points by hole 3x3 around duplicit point
|
||||
if (!duplicits.empty()) {
|
||||
holes.reserve(duplicits.size());
|
||||
for (const Point &p : duplicits) {
|
||||
Slic3r::Polygon hole(pts_3x3);
|
||||
hole.translate(p);
|
||||
holes.push_back(hole);
|
||||
}
|
||||
}
|
||||
|
||||
holes = Slic3r::union_(holes);
|
||||
res = Slic3r::diff_ex(res, holes, ApplySafetyOffset::Yes);
|
||||
}
|
||||
/* VISUALIZATION of BAD symbols for debug
|
||||
{
|
||||
double svg_scale = Emboss::SHAPE_SCALE / unscale<double>(1.);
|
||||
BoundingBox bb(to_points(shape));
|
||||
//bb.scale(svg_scale);
|
||||
SVG svg("C:/data/temp/fix_self_intersection.svg", bb);
|
||||
svg.draw(shape);
|
||||
// svg.draw(polygons, "orange");
|
||||
svg.draw(res, "green");
|
||||
|
||||
svg.draw(duplicits, "lightgray", 13 / Emboss::SHAPE_SCALE);
|
||||
Points duplicits3 = collect_duplications(to_points(res));
|
||||
svg.draw(duplicits3, "black", 7 / Emboss::SHAPE_SCALE);
|
||||
|
||||
Pointfs pts2 = intersection_points(res);
|
||||
Points pts_i; pts_i.reserve(pts2.size());
|
||||
for (auto p : pts2) pts_i.push_back(p.cast<int>());
|
||||
svg.draw(pts_i, "red", 8 / Emboss::SHAPE_SCALE);
|
||||
} //*/
|
||||
|
||||
if (max_iteration == 0) {
|
||||
assert(false);
|
||||
BoundingBox bb = get_extents(shape);
|
||||
Point size = bb.size();
|
||||
if (size.x() < 10) bb.max.x() += 10;
|
||||
if (size.y() < 10) bb.max.y() += 10;
|
||||
|
||||
Polygon rect({ // CCW
|
||||
bb.min,
|
||||
{bb.max.x(), bb.min.y()},
|
||||
bb.max,
|
||||
{bb.min.x(), bb.max.y()}
|
||||
});
|
||||
|
||||
Point offset = bb.size()*0.1;
|
||||
Polygon hole({ // CW
|
||||
bb.min + offset,
|
||||
{bb.min.x()+offset.x(), bb.max.y()-offset.y()},
|
||||
bb.max - offset,
|
||||
{bb.max.x()-offset.x(), bb.min.y()+offset.y()}
|
||||
});
|
||||
ExPolygon res(rect, hole);
|
||||
// BAD symbol
|
||||
return {res};
|
||||
}
|
||||
|
||||
assert(intersection_points(res).empty());
|
||||
assert(collect_duplications(to_points(res)).empty());
|
||||
return res;
|
||||
}
|
||||
|
||||
std::optional<Emboss::Glyph> Private::get_glyph(const stbtt_fontinfo &font_info, int unicode_letter, float flatness)
|
||||
{
|
||||
int glyph_index = stbtt_FindGlyphIndex(&font_info, unicode_letter);
|
||||
|
@ -127,12 +226,7 @@ std::optional<Emboss::Glyph> Private::get_glyph(const stbtt_fontinfo &font_info,
|
|||
std::reverse(pts.begin(), pts.end());
|
||||
glyph_polygons.emplace_back(pts);
|
||||
}
|
||||
|
||||
// fix for bad defined fonts
|
||||
glyph.shape = Slic3r::union_ex(glyph_polygons);
|
||||
|
||||
// inner cw - hole
|
||||
// outer ccw - contour
|
||||
glyph.shape = Emboss::heal_shape(glyph_polygons);
|
||||
return glyph;
|
||||
}
|
||||
|
||||
|
@ -175,28 +269,25 @@ const Emboss::Glyph* Private::get_glyph(
|
|||
glyph_opt->left_side_bearing =
|
||||
static_cast<int>(glyph_opt->left_side_bearing / Emboss::SHAPE_SCALE);
|
||||
|
||||
if (font_prop.boldness.has_value()) {
|
||||
float delta = *font_prop.boldness / Emboss::SHAPE_SCALE / font_prop.size_in_mm;
|
||||
glyph_opt->shape = offset_ex(glyph_opt->shape, delta);
|
||||
}
|
||||
|
||||
if (font_prop.skew.has_value()) {
|
||||
const float &ratio = *font_prop.skew;
|
||||
auto skew = [&ratio](Slic3r::Polygon &polygon) {
|
||||
for (Slic3r::Point &p : polygon.points) { p.x() += p.y() * ratio; }
|
||||
};
|
||||
for (ExPolygon &expolygon : glyph_opt->shape) {
|
||||
skew(expolygon.contour);
|
||||
for (Slic3r::Polygon &hole : expolygon.holes) skew(hole);
|
||||
if (!glyph_opt->shape.empty()) {
|
||||
if (font_prop.boldness.has_value()) {
|
||||
float delta = *font_prop.boldness / Emboss::SHAPE_SCALE /
|
||||
font_prop.size_in_mm;
|
||||
glyph_opt->shape = Slic3r::union_ex(offset_ex(glyph_opt->shape, delta));
|
||||
}
|
||||
if (font_prop.skew.has_value()) {
|
||||
const float &ratio = *font_prop.skew;
|
||||
auto skew = [&ratio](Slic3r::Polygon &polygon) {
|
||||
for (Slic3r::Point &p : polygon.points) {
|
||||
p.x() += p.y() * ratio;
|
||||
}
|
||||
};
|
||||
for (ExPolygon &expolygon : glyph_opt->shape) {
|
||||
skew(expolygon.contour);
|
||||
for (Slic3r::Polygon &hole : expolygon.holes) skew(hole);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// union of shape
|
||||
// (for sure) I do not believe in font corectness
|
||||
// modification like bold or skew could create artefacts
|
||||
glyph_opt->shape = Slic3r::union_ex(glyph_opt->shape);
|
||||
// unify multipoints with similar position. Could appear after union
|
||||
dilate_to_unique_points(glyph_opt->shape);
|
||||
auto it = cache.insert({unicode, std::move(*glyph_opt)});
|
||||
assert(it.second);
|
||||
return &it.first->second;
|
||||
|
@ -208,72 +299,6 @@ EmbossStyle Private::create_style(std::wstring name, std::wstring path) {
|
|||
EmbossStyle::Type::file_path, FontProp() };
|
||||
}
|
||||
|
||||
ExPolygons Private::dilate_to_unique_points(ExPolygons &expolygons)
|
||||
{
|
||||
std::set<Point> points;
|
||||
std::set<Point> multi_points;
|
||||
auto find_multipoint = [&points, &multi_points](const Points &pts) {
|
||||
for (const Point &p : pts) {
|
||||
auto it = points.find(p);
|
||||
if (it != points.end())
|
||||
multi_points.insert(p);
|
||||
else
|
||||
points.insert(p);
|
||||
}
|
||||
};
|
||||
for (const ExPolygon &expolygon : expolygons) {
|
||||
find_multipoint(expolygon.contour.points);
|
||||
for (const Slic3r::Polygon &hole : expolygon.holes)
|
||||
find_multipoint(hole.points);
|
||||
}
|
||||
// speed up, no multipoints
|
||||
if (multi_points.empty()) return expolygons;
|
||||
|
||||
// CCW rectangle around zero with size 3*3 px for dilatation
|
||||
const Points rect_3_3{Point(1, 1), Point(-1, 1), Point(-1, -1), Point(1, -1)};
|
||||
const Points rect_side{Point(1, 0), Point(0, 1), Point(-1, 0), Point(0, -1)};
|
||||
|
||||
// all new added points for reduction
|
||||
std::set<Point> rects_points;
|
||||
|
||||
// extends expolygons with dilatation rectangle
|
||||
expolygons.reserve(expolygons.size() + multi_points.size());
|
||||
for (const Point &multi_point : multi_points) {
|
||||
Slic3r::Polygon rect(rect_3_3); // copy points
|
||||
rect.translate(multi_point);
|
||||
for (const Point& p : rect.points) rects_points.insert(p);
|
||||
// add side point to be sure with result
|
||||
for (const Point& p : rect_side) rects_points.insert(p + multi_point);
|
||||
expolygons.emplace_back(rect);
|
||||
}
|
||||
ExPolygons result = union_ex(expolygons);
|
||||
|
||||
// reduce new created close points
|
||||
auto reduce_close_points = [&rects_points](Points &pts) {
|
||||
bool is_first = false;
|
||||
size_t offset = 0;
|
||||
bool is_prev_rect = false;
|
||||
for (size_t i = 0; i < pts.size(); i++) {
|
||||
const Point &p = pts[i];
|
||||
bool is_rect = (rects_points.find(p) != rects_points.end());
|
||||
if (is_prev_rect && is_rect) ++offset;
|
||||
if (offset != 0) pts[i - offset] = p;
|
||||
if (i == 0 && is_rect) is_first = true;
|
||||
is_prev_rect = is_rect;
|
||||
}
|
||||
// remove last
|
||||
if (is_first && is_prev_rect) ++offset;
|
||||
if (offset != 0)
|
||||
pts.erase(pts.begin() + (pts.size() - offset), pts.end());
|
||||
};
|
||||
for (ExPolygon &expolygon : result) {
|
||||
reduce_close_points(expolygon.contour.points);
|
||||
for (Slic3r::Polygon &hole : expolygon.holes)
|
||||
reduce_close_points(hole.points);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Point Private::to_point(const stbtt__point &point) {
|
||||
return Point(static_cast<int>(std::round(point.x / Emboss::SHAPE_SCALE)),
|
||||
static_cast<int>(std::round(point.y / Emboss::SHAPE_SCALE)));
|
||||
|
@ -627,7 +652,6 @@ ExPolygons Emboss::text2shapes(FontFileWithCache &font_with_cache,
|
|||
const FontProp &font_prop)
|
||||
{
|
||||
assert(font_with_cache.has_value());
|
||||
|
||||
std::optional<stbtt_fontinfo> font_info_opt;
|
||||
Point cursor(0, 0);
|
||||
ExPolygons result;
|
||||
|
@ -671,8 +695,7 @@ ExPolygons Emboss::text2shapes(FontFileWithCache &font_with_cache,
|
|||
cursor.x() += glyph_ptr->advance_width;
|
||||
expolygons_append(result, std::move(expolygons));
|
||||
}
|
||||
result = Slic3r::union_ex(result);
|
||||
return Private::dilate_to_unique_points(result);
|
||||
return Slic3r::union_ex(result);
|
||||
}
|
||||
|
||||
void Emboss::apply_transformation(const FontProp &font_prop,
|
||||
|
@ -777,37 +800,136 @@ double Emboss::get_shape_scale(const FontProp &fp, const FontFile &ff)
|
|||
return scale * Emboss::SHAPE_SCALE;
|
||||
}
|
||||
|
||||
indexed_triangle_set Emboss::polygons2model(const ExPolygons &shape2d,
|
||||
const IProjection &projection)
|
||||
static void store_trinagulation(
|
||||
const ExPolygons &shape,
|
||||
const std::vector<Vec3i> &triangles,
|
||||
const char *file_name = "C:/data/temp/triangulation.svg",
|
||||
double scale = 1e5)
|
||||
{
|
||||
indexed_triangle_set result;
|
||||
size_t count_point = count_points(shape2d);
|
||||
result.vertices.reserve(2 * count_point);
|
||||
BoundingBox bb;
|
||||
for (const auto &expoly : shape) bb.merge(expoly.contour.points);
|
||||
bb.scale(scale);
|
||||
SVG svg_vis(file_name, bb);
|
||||
svg_vis.draw(shape, "gray", .7f);
|
||||
Points pts = to_points(shape);
|
||||
svg_vis.draw(pts, "black", 4 * scale);
|
||||
for (const Vec3i &t : triangles) {
|
||||
Slic3r::Polygon triangle({pts[t[0]], pts[t[1]], pts[t[2]]});
|
||||
triangle.scale(scale);
|
||||
svg_vis.draw(triangle, "green");
|
||||
}
|
||||
}
|
||||
namespace priv {
|
||||
|
||||
void add_quad(uint32_t i1,
|
||||
uint32_t i2,
|
||||
indexed_triangle_set &result,
|
||||
uint32_t count_point)
|
||||
{
|
||||
// bottom indices
|
||||
uint32_t i1_ = i1 + count_point;
|
||||
uint32_t i2_ = i2 + count_point;
|
||||
result.indices.emplace_back(i2, i2_, i1);
|
||||
result.indices.emplace_back(i1_, i1, i2_);
|
||||
};
|
||||
|
||||
indexed_triangle_set polygons2model_unique(
|
||||
const ExPolygons &shape2d,
|
||||
const Emboss::IProjection &projection,
|
||||
const Points &points)
|
||||
{
|
||||
// CW order of triangle indices
|
||||
std::vector<Vec3i> shape_triangles=Triangulation::triangulate(shape2d, points);
|
||||
uint32_t count_point = points.size();
|
||||
|
||||
indexed_triangle_set result;
|
||||
result.vertices.reserve(2 * count_point);
|
||||
std::vector<Vec3f> &front_points = result.vertices; // alias
|
||||
std::vector<Vec3f> back_points;
|
||||
back_points.reserve(count_point);
|
||||
|
||||
for (const Point &p : points) {
|
||||
auto p2 = projection.create_front_back(p);
|
||||
front_points.push_back(p2.first);
|
||||
back_points.push_back(p2.second);
|
||||
}
|
||||
|
||||
// insert back points, front are already in
|
||||
result.vertices.insert(result.vertices.end(),
|
||||
std::make_move_iterator(back_points.begin()),
|
||||
std::make_move_iterator(back_points.end()));
|
||||
result.indices.reserve(shape_triangles.size() * 2 + points.size() * 2);
|
||||
// top triangles - change to CCW
|
||||
for (const Vec3i &t : shape_triangles)
|
||||
result.indices.emplace_back(t.x(), t.z(), t.y());
|
||||
// bottom triangles - use CW
|
||||
for (const Vec3i &t : shape_triangles)
|
||||
result.indices.emplace_back(t.x() + count_point,
|
||||
t.y() + count_point,
|
||||
t.z() + count_point);
|
||||
|
||||
// quads around - zig zag by triangles
|
||||
size_t polygon_offset = 0;
|
||||
auto add_quads = [&polygon_offset,&result, &count_point]
|
||||
(const Slic3r::Polygon& polygon) {
|
||||
uint32_t polygon_points = polygon.points.size();
|
||||
// previous index
|
||||
uint32_t prev = polygon_offset + polygon_points - 1;
|
||||
for (uint32_t p = 0; p < polygon_points; ++p) {
|
||||
uint32_t index = polygon_offset + p;
|
||||
add_quad(prev, index, result, count_point);
|
||||
prev = index;
|
||||
}
|
||||
polygon_offset += polygon_points;
|
||||
};
|
||||
|
||||
for (const ExPolygon &expolygon : shape2d) {
|
||||
add_quads(expolygon.contour);
|
||||
for (const Slic3r::Polygon &hole : expolygon.holes) add_quads(hole);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
indexed_triangle_set polygons2model_duplicit(
|
||||
const ExPolygons &shape2d,
|
||||
const Emboss::IProjection &projection,
|
||||
const Points &points,
|
||||
const Points &duplicits)
|
||||
{
|
||||
// CW order of triangle indices
|
||||
std::vector<uint32_t> changes = Triangulation::create_changes(points, duplicits);
|
||||
std::vector<Vec3i> shape_triangles = Triangulation::triangulate(shape2d, points, changes);
|
||||
uint32_t count_point = *std::max_element(changes.begin(), changes.end()) + 1;
|
||||
|
||||
indexed_triangle_set result;
|
||||
result.vertices.reserve(2 * count_point);
|
||||
std::vector<Vec3f> &front_points = result.vertices;
|
||||
std::vector<Vec3f> back_points;
|
||||
back_points.reserve(count_point);
|
||||
|
||||
auto insert_point = [&projection, &front_points,
|
||||
&back_points](const Polygon& polygon) {
|
||||
for (const Point& p : polygon.points) {
|
||||
auto p2 = projection.create_front_back(p);
|
||||
front_points.emplace_back(p2.first);
|
||||
back_points.emplace_back(p2.second);
|
||||
}
|
||||
};
|
||||
for (const ExPolygon &expolygon : shape2d) {
|
||||
insert_point(expolygon.contour);
|
||||
for (const Polygon &hole : expolygon.holes) insert_point(hole);
|
||||
uint32_t max_index = std::numeric_limits<uint32_t>::max();
|
||||
for (uint32_t i = 0; i < changes.size(); ++i) {
|
||||
uint32_t index = changes[i];
|
||||
if (max_index != std::numeric_limits<uint32_t>::max() &&
|
||||
index <= max_index) continue; // duplicit point
|
||||
assert(index == max_index + 1);
|
||||
assert(front_points.size() == index);
|
||||
assert(back_points.size() == index);
|
||||
max_index = index;
|
||||
const Point &p = points[i];
|
||||
auto p2 = projection.create_front_back(p);
|
||||
front_points.push_back(p2.first);
|
||||
back_points.push_back(p2.second);
|
||||
}
|
||||
assert(max_index+1 == count_point);
|
||||
|
||||
// insert back points, front are already in
|
||||
result.vertices.insert(result.vertices.end(),
|
||||
std::make_move_iterator(back_points.begin()),
|
||||
std::make_move_iterator(back_points.end()));
|
||||
|
||||
// CW order of triangle indices
|
||||
std::vector<Vec3i> shape_triangles = Triangulation::triangulate(shape2d);
|
||||
result.indices.reserve(shape_triangles.size() * 2 + count_point * 2);
|
||||
result.indices.reserve(shape_triangles.size() * 2 + points.size() * 2);
|
||||
// top triangles - change to CCW
|
||||
for (const Vec3i &t : shape_triangles)
|
||||
result.indices.emplace_back(t.x(), t.z(), t.y());
|
||||
|
@ -818,28 +940,37 @@ indexed_triangle_set Emboss::polygons2model(const ExPolygons &shape2d,
|
|||
|
||||
// quads around - zig zag by triangles
|
||||
size_t polygon_offset = 0;
|
||||
auto add_quads = [&result,&polygon_offset, count_point](const Polygon& polygon) {
|
||||
auto add_quads = [&polygon_offset, &result, count_point, &changes]
|
||||
(const Slic3r::Polygon &polygon) {
|
||||
uint32_t polygon_points = polygon.points.size();
|
||||
for (uint32_t p = 0; p < polygon_points; p++) {
|
||||
uint32_t i = polygon_offset + p;
|
||||
// previous index
|
||||
uint32_t ip = (p == 0) ? (polygon_offset + polygon_points - 1) : (i - 1);
|
||||
// bottom indices
|
||||
uint32_t i2 = i + count_point;
|
||||
uint32_t ip2 = ip + count_point;
|
||||
|
||||
result.indices.emplace_back(i, i2, ip);
|
||||
result.indices.emplace_back(ip2, ip, i2);
|
||||
// previous index
|
||||
uint32_t prev = changes[polygon_offset + polygon_points - 1];
|
||||
for (uint32_t p = 0; p < polygon_points; ++p) {
|
||||
uint32_t index = changes[polygon_offset + p];
|
||||
if (prev == index) continue;
|
||||
add_quad(prev, index, result, count_point);
|
||||
prev = index;
|
||||
}
|
||||
polygon_offset += polygon_points;
|
||||
};
|
||||
|
||||
|
||||
for (const ExPolygon &expolygon : shape2d) {
|
||||
add_quads(expolygon.contour);
|
||||
for (const Polygon &hole : expolygon.holes) add_quads(hole);
|
||||
for (const Slic3r::Polygon &hole : expolygon.holes) add_quads(hole);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace priv
|
||||
|
||||
indexed_triangle_set Emboss::polygons2model(const ExPolygons &shape2d,
|
||||
const IProjection &projection)
|
||||
{
|
||||
Points points = to_points(shape2d);
|
||||
Points duplicits = collect_duplications(points);
|
||||
return (duplicits.empty()) ?
|
||||
priv::polygons2model_unique(shape2d, projection, points) :
|
||||
priv::polygons2model_duplicit(shape2d, projection, points, duplicits);
|
||||
}
|
||||
|
||||
std::pair<Vec3f, Vec3f> Emboss::ProjectZ::create_front_back(const Point &p) const
|
||||
{
|
||||
|
|
|
@ -155,6 +155,13 @@ public:
|
|||
/// <returns>Inner polygon cw(outer ccw)</returns>
|
||||
static ExPolygons text2shapes(FontFileWithCache &font, const char *text, const FontProp &font_prop);
|
||||
|
||||
/// <summary>
|
||||
/// Fix intersections and self intersections in polygons glyph shape
|
||||
/// </summary>
|
||||
/// <param name="shape">Input shape to heal</param>
|
||||
/// <returns>Healed shapes</returns>
|
||||
static ExPolygons heal_shape(const Polygons &shape);
|
||||
|
||||
/// <summary>
|
||||
/// Use data from font property to modify transformation
|
||||
/// </summary>
|
||||
|
|
|
@ -129,6 +129,19 @@ inline Lines to_lines(const ExPolygons &src)
|
|||
return lines;
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
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: Bentley–Ottmann_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_
|
|
@ -36,9 +36,7 @@ void NSVGUtils::flatten_cubic_bez(Polygon &polygon,
|
|||
flatten_cubic_bez(polygon, tessTol, p1234, p234, p34, p4, level);
|
||||
}
|
||||
|
||||
ExPolygons NSVGUtils::to_ExPolygons(NSVGimage *image,
|
||||
float tessTol,
|
||||
int max_level)
|
||||
Polygons NSVGUtils::to_polygons(NSVGimage *image, float tessTol, int max_level)
|
||||
{
|
||||
Polygons polygons;
|
||||
for (NSVGshape *shape = image->shapes; shape != NULL;
|
||||
|
@ -59,14 +57,23 @@ ExPolygons NSVGUtils::to_ExPolygons(NSVGimage *image,
|
|||
flatten_cubic_bez(polygon, tessTol, p1, p2, p3, p4,
|
||||
max_level);
|
||||
}
|
||||
if (path->closed) {
|
||||
if (path->closed && !polygon.empty()) {
|
||||
polygons.push_back(polygon);
|
||||
polygon = Slic3r::Polygon();
|
||||
}
|
||||
}
|
||||
}
|
||||
polygons.push_back(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)
|
||||
|
|
|
@ -25,6 +25,10 @@ public:
|
|||
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_
|
||||
|
|
|
@ -158,6 +158,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);
|
||||
|
|
|
@ -260,6 +260,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)
|
||||
|
|
|
@ -377,4 +377,10 @@ void SVG::export_expolygons(const char *path, const std::vector<std::pair<Slic3r
|
|||
svg.Close();
|
||||
}
|
||||
|
||||
float SVG::to_svg_coord(float x)
|
||||
{
|
||||
// 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,35 +1,90 @@
|
|||
#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 Slic3r::Private {
|
||||
|
||||
inline void insert_points(Points& points, const Polygon &polygon) {
|
||||
points.insert(points.end(), polygon.points.begin(), polygon.points.end());
|
||||
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;
|
||||
for (uint32_t i = 1; i < pts.size(); ++i) {
|
||||
uint32_t i2 = i + offset;
|
||||
edges.push_back({i2 - 1, i2});
|
||||
}
|
||||
uint32_t size = static_cast<uint32_t>(pts.size());
|
||||
// add connection from first to last point
|
||||
edges.push_back({offset + size - 1, offset});
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace Private
|
||||
|
||||
#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
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
static 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();
|
||||
}
|
||||
|
||||
Triangulation::Indices Triangulation::triangulate(const Points &points,
|
||||
const HalfEdges &constrained_half_edges)
|
||||
{
|
||||
assert(!points.empty());
|
||||
assert(!constrained_half_edges.empty());
|
||||
// edges can NOT contain bidirectional constrained
|
||||
assert(!has_bidirectional_constrained(constrained_half_edges));
|
||||
// constrained must be sorted
|
||||
assert(std::is_sorted(constrained_half_edges.begin(),
|
||||
constrained_half_edges.end()));
|
||||
// check that there is only unique poistion of points
|
||||
assert(std::adjacent_find(points.begin(), points.end()) == points.end());
|
||||
assert(!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>;
|
||||
|
@ -71,51 +126,73 @@ Triangulation::Indices Triangulation::triangulate(const Points &points,
|
|||
// 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)) {
|
||||
auto key = std::make_pair(fh->vertex((i + 2) % 3)->info(), fh->vertex((i + 1) % 3)->info());
|
||||
if (auto it = std::lower_bound(constrained_half_edges.begin(), constrained_half_edges.end(), key); it != constrained_half_edges.end() && *it == key) {
|
||||
// This face contains a constrained edge and it is outside.
|
||||
for (int j = 0; j < 3; ++ j)
|
||||
fh->set_constraint(j, false);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 3; ++i) if (fh->vertex(i)->info() == 752) {
|
||||
int j = 42;
|
||||
}
|
||||
++ num_faces;
|
||||
end:;
|
||||
|
||||
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);
|
||||
auto inside = [](CDT::Face_handle &fh) { return fh->neighbor(0) != fh && (fh->is_constrained(0) || fh->is_constrained(1) || fh->is_constrained(2)); };
|
||||
for (CDT::Face_handle seed : faces)
|
||||
if (inside(seed)) {
|
||||
// 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)) {
|
||||
// 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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -141,7 +218,7 @@ Triangulation::Indices Triangulation::triangulate(const Polygons &polygons)
|
|||
uint32_t offset = 0;
|
||||
|
||||
for (const Polygon &polygon : polygons) {
|
||||
Private::insert_points(points, polygon);
|
||||
Slic3r::append(points, polygon.points);
|
||||
Private::insert_edges(edges, offset, polygon);
|
||||
}
|
||||
|
||||
|
@ -149,25 +226,90 @@ Triangulation::Indices Triangulation::triangulate(const Polygons &polygons)
|
|||
return triangulate(points, edges);
|
||||
}
|
||||
|
||||
Triangulation::Indices Triangulation::triangulate(const ExPolygons &expolygons)
|
||||
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)
|
||||
{
|
||||
size_t count = count_points(expolygons);
|
||||
Points points;
|
||||
points.reserve(count);
|
||||
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(count);
|
||||
edges.reserve(points.size());
|
||||
uint32_t offset = 0;
|
||||
|
||||
for (const ExPolygon &expolygon : expolygons) {
|
||||
Private::insert_points(points, expolygon.contour);
|
||||
for (const ExPolygon &expolygon : expolygons) {
|
||||
Private::insert_edges(edges, offset, expolygon.contour);
|
||||
for (const Polygon &hole : expolygon.holes) {
|
||||
Private::insert_points(points, hole);
|
||||
Private::insert_edges(edges, offset, hole);
|
||||
}
|
||||
}
|
||||
|
||||
for (const Polygon &hole : expolygon.holes)
|
||||
Private::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) {
|
||||
Private::insert_edges(edges, offset, expolygon.contour, changes);
|
||||
for (const Polygon &hole : expolygon.holes)
|
||||
Private::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;
|
||||
}
|
||||
|
|
|
@ -34,15 +34,37 @@ public:
|
|||
static Indices triangulate(const Polygons &polygons);
|
||||
static Indices triangulate(const ExPolygons &expolygons);
|
||||
|
||||
/// <summary>
|
||||
/// Filter out triagles without both side edge or inside half edges
|
||||
/// Main purpose: Filter out triangles which lay outside of ExPolygon
|
||||
/// given to triangulation
|
||||
/// </summary>
|
||||
/// <param name="indices">Triangles</param>
|
||||
/// <param name="half_edges">Only outer edges</param>
|
||||
static void remove_outer(Indices &indices, const HalfEdges &half_edges);
|
||||
// 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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue