Partially working implementation of custom seam backend
This commit is contained in:
parent
7844ca12fa
commit
a1fadaf955
4 changed files with 227 additions and 56 deletions
src/libslic3r
|
@ -176,27 +176,34 @@ namespace Slic3r {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int CustomSeam::get_point_status(const Point& pt, size_t layer_id) const
|
void CustomSeam::get_indices(size_t layer_id,
|
||||||
|
const Polygon& polygon,
|
||||||
|
std::vector<size_t>& enforcers_idxs,
|
||||||
|
std::vector<size_t>& blockers_idxs) const
|
||||||
{
|
{
|
||||||
// TEMPORARY - WILL BE IMPROVED
|
enforcers_idxs.clear();
|
||||||
// - quadratic algorithm
|
blockers_idxs.clear();
|
||||||
// - does not support variable layer height
|
|
||||||
|
|
||||||
if (! enforcers.empty()) {
|
// FIXME: This is quadratic and it should be improved, maybe by building
|
||||||
assert(layer_id < enforcers.size());
|
// an AABB tree (or at least utilize bounding boxes).
|
||||||
for (const ExPolygon& explg : enforcers[layer_id]) {
|
for (size_t i=0; i<polygon.points.size(); ++i) {
|
||||||
if (explg.contains(pt))
|
|
||||||
return 1;
|
if (! enforcers.empty()) {
|
||||||
|
assert(layer_id < enforcers.size());
|
||||||
|
for (const ExPolygon& explg : enforcers[layer_id]) {
|
||||||
|
if (explg.contains(polygon.points[i]))
|
||||||
|
enforcers_idxs.push_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! blockers.empty()) {
|
||||||
|
assert(layer_id < blockers.size());
|
||||||
|
for (const ExPolygon& explg : blockers[layer_id]) {
|
||||||
|
if (explg.contains(polygon.points[i]))
|
||||||
|
blockers_idxs.push_back(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (! blockers.empty()) {
|
|
||||||
assert(layer_id < blockers.size());
|
|
||||||
for (const ExPolygon& explg : blockers[layer_id]) {
|
|
||||||
if (explg.contains(pt))
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1017,12 +1024,12 @@ namespace DoExport {
|
||||||
po->project_and_append_custom_facets(true, EnforcerBlockerType::ENFORCER, custom_seam.enforcers);
|
po->project_and_append_custom_facets(true, EnforcerBlockerType::ENFORCER, custom_seam.enforcers);
|
||||||
po->project_and_append_custom_facets(true, EnforcerBlockerType::BLOCKER, custom_seam.blockers);
|
po->project_and_append_custom_facets(true, EnforcerBlockerType::BLOCKER, custom_seam.blockers);
|
||||||
}
|
}
|
||||||
for (ExPolygons& explgs : custom_seam.enforcers) {
|
const std::vector<double>& nozzle_dmrs = print.config().nozzle_diameter.values;
|
||||||
explgs = Slic3r::offset_ex(explgs, scale_(0.5));
|
float max_nozzle_dmr = *std::max_element(nozzle_dmrs.begin(), nozzle_dmrs.end());
|
||||||
}
|
for (ExPolygons& explgs : custom_seam.enforcers)
|
||||||
for (ExPolygons& explgs : custom_seam.blockers) {
|
explgs = Slic3r::offset_ex(explgs, scale_(max_nozzle_dmr));
|
||||||
explgs = Slic3r::offset_ex(explgs, scale_(0.5));
|
for (ExPolygons& explgs : custom_seam.blockers)
|
||||||
}
|
explgs = Slic3r::offset_ex(explgs, scale_(max_nozzle_dmr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2696,15 +2703,7 @@ static Points::const_iterator project_point_to_polygon_and_insert(Polygon &polyg
|
||||||
return polygon.points.begin() + i_min;
|
return polygon.points.begin() + i_min;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<float> polygon_parameter_by_length(const Polygon &polygon)
|
|
||||||
{
|
|
||||||
// Parametrize the polygon by its length.
|
|
||||||
std::vector<float> lengths(polygon.points.size()+1, 0.);
|
|
||||||
for (size_t i = 1; i < polygon.points.size(); ++ i)
|
|
||||||
lengths[i] = lengths[i-1] + (polygon.points[i] - polygon.points[i-1]).cast<float>().norm();
|
|
||||||
lengths.back() = lengths[lengths.size()-2] + (polygon.points.front() - polygon.points.back()).cast<float>().norm();
|
|
||||||
return lengths;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<float> polygon_angles_at_vertices(const Polygon &polygon, const std::vector<float> &lengths, float min_arm_length)
|
std::vector<float> polygon_angles_at_vertices(const Polygon &polygon, const std::vector<float> &lengths, float min_arm_length)
|
||||||
{
|
{
|
||||||
|
@ -2761,6 +2760,136 @@ std::vector<float> polygon_angles_at_vertices(const Polygon &polygon, const std:
|
||||||
return angles;
|
return angles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Go through the polygon, identify points inside support enforcers and return
|
||||||
|
// indices of points in the middle of each enforcer (measured along the contour).
|
||||||
|
static std::vector<size_t> find_enforcer_centers(const Polygon& polygon,
|
||||||
|
const std::vector<float>& lengths,
|
||||||
|
const std::vector<size_t>& enforcers_idxs)
|
||||||
|
{
|
||||||
|
std::vector<size_t> out;
|
||||||
|
assert(polygon.points.size()+1 == lengths.size());
|
||||||
|
assert(std::is_sorted(enforcers_idxs.begin(), enforcers_idxs.end()));
|
||||||
|
if (polygon.size() < 2 || enforcers_idxs.empty())
|
||||||
|
return out;
|
||||||
|
|
||||||
|
auto get_center_idx = [&polygon, &lengths](size_t start_idx, size_t end_idx) -> size_t {
|
||||||
|
assert(end_idx >= start_idx);
|
||||||
|
if (start_idx == end_idx)
|
||||||
|
return start_idx;
|
||||||
|
float t_c = lengths[start_idx] + 0.5f * (lengths[end_idx] - lengths[start_idx]);
|
||||||
|
auto it = std::lower_bound(lengths.begin() + start_idx, lengths.begin() + end_idx, t_c);
|
||||||
|
int ret = it - lengths.begin();
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
int last_enforcer_start_idx = enforcers_idxs.front();
|
||||||
|
bool last_pt_in_list = enforcers_idxs.back() == polygon.points.size() - 1;
|
||||||
|
|
||||||
|
for (size_t i=0; i<enforcers_idxs.size()-1; ++i) {
|
||||||
|
if ((i == enforcers_idxs.size() - 1)
|
||||||
|
|| enforcers_idxs[i+1] != enforcers_idxs[i] + 1) {
|
||||||
|
// i is last point of current enforcer
|
||||||
|
out.push_back(get_center_idx(last_enforcer_start_idx, enforcers_idxs[i]));
|
||||||
|
last_enforcer_start_idx = enforcers_idxs[i+1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_pt_in_list) {
|
||||||
|
// last point is an enforcer - not yet accounted for.
|
||||||
|
if (enforcers_idxs.front() != 0) {
|
||||||
|
size_t center_idx = get_center_idx(last_enforcer_start_idx, enforcers_idxs.back());
|
||||||
|
out.push_back(center_idx);
|
||||||
|
} else {
|
||||||
|
// Wrap-around. Update first center already found.
|
||||||
|
if (out.empty()) {
|
||||||
|
// Probably an enforcer around the whole contour. Return nothing.
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find last point of the enforcer at the beginning:
|
||||||
|
size_t idx = 0;
|
||||||
|
while (enforcers_idxs[idx]+1 == enforcers_idxs[idx+1])
|
||||||
|
++idx;
|
||||||
|
|
||||||
|
float t_s = lengths[last_enforcer_start_idx];
|
||||||
|
float t_e = lengths[idx];
|
||||||
|
float half_dist = 0.5f * (t_e + lengths.back() - t_s);
|
||||||
|
float t_c = (half_dist > t_e) ? t_s + half_dist : t_e - half_dist;
|
||||||
|
|
||||||
|
auto it = std::lower_bound(lengths.begin(), lengths.end(), t_c);
|
||||||
|
out[0] = it - lengths.begin();
|
||||||
|
if (out[0] == lengths.size() - 1)
|
||||||
|
--out[0];
|
||||||
|
assert(out[0] < lengths.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CustomSeam::penalize_polygon(const Polygon& polygon,
|
||||||
|
std::vector<float>& penalties,
|
||||||
|
const std::vector<float>& lengths,
|
||||||
|
int layer_id) const
|
||||||
|
{
|
||||||
|
std::vector<size_t> enforcers_idxs;
|
||||||
|
std::vector<size_t> blockers_idxs;
|
||||||
|
this->get_indices(layer_id, polygon, enforcers_idxs, blockers_idxs);
|
||||||
|
|
||||||
|
for (size_t i : enforcers_idxs) {
|
||||||
|
assert(i < penalties.size());
|
||||||
|
penalties[i] -= float(ENFORCER_BLOCKER_PENALTY);
|
||||||
|
}
|
||||||
|
for (size_t i : blockers_idxs) {
|
||||||
|
assert(i < penalties.size());
|
||||||
|
penalties[i] += float(ENFORCER_BLOCKER_PENALTY);
|
||||||
|
}
|
||||||
|
std::vector<size_t> enf_centers = find_enforcer_centers(polygon, lengths, enforcers_idxs);
|
||||||
|
for (size_t idx : enf_centers) {
|
||||||
|
assert(idx < penalties.size());
|
||||||
|
penalties[idx] -= 1000.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// //////////////////////
|
||||||
|
// std::ostringstream os;
|
||||||
|
// os << std::setw(3) << std::setfill('0') << layer_id;
|
||||||
|
// int a = scale_(15.);
|
||||||
|
// SVG svg("custom_seam" + os.str() + ".svg", BoundingBox(Point(-a, -a), Point(a, a)));
|
||||||
|
// /*if (! m_custom_seam.enforcers.empty())
|
||||||
|
// svg.draw(m_custom_seam.enforcers[layer_id], "blue");
|
||||||
|
// if (! m_custom_seam.blockers.empty())
|
||||||
|
// svg.draw(m_custom_seam.blockers[layer_id], "red");*/
|
||||||
|
|
||||||
|
// size_t min_idx = std::min_element(penalties.begin(), penalties.end()) - penalties.begin();
|
||||||
|
|
||||||
|
// //svg.draw(polygon.points[idx_min], "red", 6e5);
|
||||||
|
// for (size_t i=0; i<polygon.points.size(); ++i) {
|
||||||
|
// std::string fill;
|
||||||
|
// coord_t size = 0;
|
||||||
|
// if (min_idx == i) {
|
||||||
|
// fill = "yellow";
|
||||||
|
// size = 5e5;
|
||||||
|
// } else {
|
||||||
|
// fill = (std::find(enforcers_idxs.begin(), enforcers_idxs.end(), i) != enforcers_idxs.end() ? "green" : "black");
|
||||||
|
// if (std::find(enf_centers.begin(), enf_centers.end(), i) != enf_centers.end()) {
|
||||||
|
// size = 5e5;
|
||||||
|
// fill = "blue";
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if (i != 0)
|
||||||
|
// svg.draw(polygon.points[i], fill, size);
|
||||||
|
// else
|
||||||
|
// svg.draw(polygon.points[i], "red", 5e5);
|
||||||
|
// }
|
||||||
|
// ////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, double speed, std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid)
|
std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, double speed, std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid)
|
||||||
{
|
{
|
||||||
// get a copy; don't modify the orientation of the original loop object otherwise
|
// get a copy; don't modify the orientation of the original loop object otherwise
|
||||||
|
@ -2804,6 +2933,12 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||||
const coordf_t nozzle_dmr = EXTRUDER_CONFIG(nozzle_diameter);
|
const coordf_t nozzle_dmr = EXTRUDER_CONFIG(nozzle_diameter);
|
||||||
const coord_t nozzle_r = coord_t(scale_(0.5 * nozzle_dmr) + 0.5);
|
const coord_t nozzle_r = coord_t(scale_(0.5 * nozzle_dmr) + 0.5);
|
||||||
|
|
||||||
|
if (m_custom_seam.is_on_layer(m_layer->id())) {
|
||||||
|
// Seam enf/blockers can begin and end in between the original vertices.
|
||||||
|
// Let add extra points in between and update the leghths.
|
||||||
|
polygon.densify(scale_(0.2f));
|
||||||
|
}
|
||||||
|
|
||||||
// Retrieve the last start position for this object.
|
// Retrieve the last start position for this object.
|
||||||
float last_pos_weight = 1.f;
|
float last_pos_weight = 1.f;
|
||||||
|
|
||||||
|
@ -2829,7 +2964,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parametrize the polygon by its length.
|
// Parametrize the polygon by its length.
|
||||||
std::vector<float> lengths = polygon_parameter_by_length(polygon);
|
std::vector<float> lengths = polygon.parameter_by_length();
|
||||||
|
|
||||||
// For each polygon point, store a penalty.
|
// For each polygon point, store a penalty.
|
||||||
// First calculate the angles, store them as penalties. The angles are caluculated over a minimum arm length of nozzle_r.
|
// First calculate the angles, store them as penalties. The angles are caluculated over a minimum arm length of nozzle_r.
|
||||||
|
@ -2870,8 +3005,8 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||||
// Penalty for overhangs.
|
// Penalty for overhangs.
|
||||||
if (lower_layer_edge_grid && (*lower_layer_edge_grid)) {
|
if (lower_layer_edge_grid && (*lower_layer_edge_grid)) {
|
||||||
// Use the edge grid distance field structure over the lower layer to calculate overhangs.
|
// Use the edge grid distance field structure over the lower layer to calculate overhangs.
|
||||||
coord_t nozzle_r = coord_t(floor(scale_(0.5 * nozzle_dmr) + 0.5));
|
coord_t nozzle_r = coord_t(std::floor(scale_(0.5 * nozzle_dmr) + 0.5));
|
||||||
coord_t search_r = coord_t(floor(scale_(0.8 * nozzle_dmr) + 0.5));
|
coord_t search_r = coord_t(std::floor(scale_(0.8 * nozzle_dmr) + 0.5));
|
||||||
for (size_t i = 0; i < polygon.points.size(); ++ i) {
|
for (size_t i = 0; i < polygon.points.size(); ++ i) {
|
||||||
const Point &p = polygon.points[i];
|
const Point &p = polygon.points[i];
|
||||||
coordf_t dist;
|
coordf_t dist;
|
||||||
|
@ -2888,10 +3023,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||||
|
|
||||||
// Penalty according to custom seam selection. This one is huge compared to
|
// Penalty according to custom seam selection. This one is huge compared to
|
||||||
// the others so that points outside enforcers/inside blockers never win.
|
// the others so that points outside enforcers/inside blockers never win.
|
||||||
for (size_t i = 0; i < polygon.points.size(); ++ i) {
|
m_custom_seam.penalize_polygon(polygon, penalties, lengths, m_layer->id());
|
||||||
const Point &p = polygon.points[i];
|
|
||||||
penalties[i] -= float(100000 * m_custom_seam.get_point_status(p, m_layer->id()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find a point with a minimum penalty.
|
// Find a point with a minimum penalty.
|
||||||
size_t idx_min = std::min_element(penalties.begin(), penalties.end()) - penalties.begin();
|
size_t idx_min = std::min_element(penalties.begin(), penalties.end()) - penalties.begin();
|
||||||
|
@ -2906,7 +3038,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||||
float penalty_max = std::max(penalty_min, penalty_aligned);
|
float penalty_max = std::max(penalty_min, penalty_aligned);
|
||||||
float penalty_diff_rel = (penalty_max == 0.f) ? 0.f : penalty_diff_abs / penalty_max;
|
float penalty_diff_rel = (penalty_max == 0.f) ? 0.f : penalty_diff_abs / penalty_max;
|
||||||
// printf("Align seams, penalty aligned: %f, min: %f, diff abs: %f, diff rel: %f\n", penalty_aligned, penalty_min, penalty_diff_abs, penalty_diff_rel);
|
// printf("Align seams, penalty aligned: %f, min: %f, diff abs: %f, diff rel: %f\n", penalty_aligned, penalty_min, penalty_diff_abs, penalty_diff_rel);
|
||||||
if (penalty_diff_rel < 0.05) {
|
if (std::abs(penalty_diff_rel) < 0.05) {
|
||||||
// Penalty of the aligned point is very close to the minimum penalty.
|
// Penalty of the aligned point is very close to the minimum penalty.
|
||||||
// Align the seams as accurately as possible.
|
// Align the seams as accurately as possible.
|
||||||
idx_min = last_pos_proj_idx;
|
idx_min = last_pos_proj_idx;
|
||||||
|
@ -2914,19 +3046,6 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||||
m_seam_position[m_layer->object()] = polygon.points[idx_min];
|
m_seam_position[m_layer->object()] = polygon.points[idx_min];
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////
|
|
||||||
// int layer_id = m_layer->id();
|
|
||||||
// std::ostringstream os;
|
|
||||||
// os << std::setw(3) << std::setfill('0') << layer_id;
|
|
||||||
// int a = scale_(15.);
|
|
||||||
// SVG svg("custom_seam" + os.str() + ".svg", BoundingBox(Point(-a, -a), Point(a, a)));
|
|
||||||
// if (! m_custom_seam.enforcers.empty())
|
|
||||||
// svg.draw(m_custom_seam.enforcers[layer_id], "blue");
|
|
||||||
// if (! m_custom_seam.blockers.empty())
|
|
||||||
// svg.draw(m_custom_seam.blockers[layer_id], "red");
|
|
||||||
// svg.draw(polygon.points, "black");
|
|
||||||
////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
// Export the contour into a SVG file.
|
// Export the contour into a SVG file.
|
||||||
#if 0
|
#if 0
|
||||||
|
|
|
@ -74,9 +74,21 @@ struct CustomSeam {
|
||||||
std::vector<ExPolygons> enforcers;
|
std::vector<ExPolygons> enforcers;
|
||||||
std::vector<ExPolygons> blockers;
|
std::vector<ExPolygons> blockers;
|
||||||
|
|
||||||
// Finds whether the point is inside an enforcer/blockers.
|
// Get indices of points inside enforcers and blockers.
|
||||||
// Returns +1, 0 or -1.
|
void get_indices(size_t layer_id,
|
||||||
int get_point_status(const Point& pt, size_t layer_id) const;
|
const Polygon& polygon,
|
||||||
|
std::vector<size_t>& enforcers_idxs,
|
||||||
|
std::vector<size_t>& blockers_idxs) const;
|
||||||
|
bool is_on_layer(size_t layer_id) const {
|
||||||
|
return ! ((enforcers.empty() || enforcers[layer_id].empty())
|
||||||
|
&& (blockers.empty() || blockers[layer_id].empty()));
|
||||||
|
}
|
||||||
|
void penalize_polygon(const Polygon& polygon,
|
||||||
|
std::vector<float>& penalties,
|
||||||
|
const std::vector<float>& lengths,
|
||||||
|
int layer_id) const;
|
||||||
|
|
||||||
|
static constexpr float ENFORCER_BLOCKER_PENALTY = 1e6;
|
||||||
};
|
};
|
||||||
|
|
||||||
class OozePrevention {
|
class OozePrevention {
|
||||||
|
|
|
@ -259,6 +259,44 @@ Point Polygon::point_projection(const Point &point) const
|
||||||
return proj;
|
return proj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<float> Polygon::parameter_by_length() const
|
||||||
|
{
|
||||||
|
// Parametrize the polygon by its length.
|
||||||
|
std::vector<float> lengths(points.size()+1, 0.);
|
||||||
|
for (size_t i = 1; i < points.size(); ++ i)
|
||||||
|
lengths[i] = lengths[i-1] + (points[i] - points[i-1]).cast<float>().norm();
|
||||||
|
lengths.back() = lengths[lengths.size()-2] + (points.front() - points.back()).cast<float>().norm();
|
||||||
|
return lengths;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Polygon::densify(float min_length, std::vector<float>* lengths_ptr)
|
||||||
|
{
|
||||||
|
std::vector<float> lengths_local;
|
||||||
|
std::vector<float>& lengths = lengths_ptr ? *lengths_ptr : lengths_local;
|
||||||
|
|
||||||
|
if (! lengths_ptr) {
|
||||||
|
// Length parametrization has not been provided. Calculate our own.
|
||||||
|
lengths = this->parameter_by_length();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(points.size() == lengths.size() - 1);
|
||||||
|
|
||||||
|
for (size_t j=1; j<=points.size(); ++j) {
|
||||||
|
bool last = j == points.size();
|
||||||
|
int i = last ? 0 : j;
|
||||||
|
|
||||||
|
if (lengths[j] - lengths[j-1] > min_length) {
|
||||||
|
Point diff = points[i] - points[j-1];
|
||||||
|
float diff_len = lengths[j] - lengths[j-1];
|
||||||
|
float r = (min_length/diff_len);
|
||||||
|
Point new_pt = points[j-1] + Point(r*diff[0], r*diff[1]);
|
||||||
|
points.insert(points.begin() + j, new_pt);
|
||||||
|
lengths.insert(lengths.begin() + j, lengths[j-1] + min_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(points.size() == lengths.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
BoundingBox get_extents(const Points &points)
|
BoundingBox get_extents(const Points &points)
|
||||||
{
|
{
|
||||||
return BoundingBox(points);
|
return BoundingBox(points);
|
||||||
|
|
|
@ -61,12 +61,14 @@ public:
|
||||||
bool contains(const Point &point) const;
|
bool contains(const Point &point) const;
|
||||||
Polygons simplify(double tolerance) const;
|
Polygons simplify(double tolerance) const;
|
||||||
void simplify(double tolerance, Polygons &polygons) const;
|
void simplify(double tolerance, Polygons &polygons) const;
|
||||||
|
void densify(float min_length, std::vector<float>* lengths = nullptr);
|
||||||
void triangulate_convex(Polygons* polygons) const;
|
void triangulate_convex(Polygons* polygons) const;
|
||||||
Point centroid() const;
|
Point centroid() const;
|
||||||
Points concave_points(double angle = PI) const;
|
Points concave_points(double angle = PI) const;
|
||||||
Points convex_points(double angle = PI) const;
|
Points convex_points(double angle = PI) const;
|
||||||
// Projection of a point onto the polygon.
|
// Projection of a point onto the polygon.
|
||||||
Point point_projection(const Point &point) const;
|
Point point_projection(const Point &point) const;
|
||||||
|
std::vector<float> parameter_by_length() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool operator==(const Polygon &lhs, const Polygon &rhs) { return lhs.points == rhs.points; }
|
inline bool operator==(const Polygon &lhs, const Polygon &rhs) { return lhs.points == rhs.points; }
|
||||||
|
|
Loading…
Reference in a new issue