|
@@ -421,6 +421,127 @@ void PathManipulator::copySelectedPath(Geom::PathBuilder *builder) |
|
|
builder->flush();
|
|
|
}
|
|
|
|
|
|
void PathManipulator::simplifyInvisible(double threshold)
|
|
|
{
|
|
|
auto tr = _getTransform().inverse();
|
|
|
auto is_small = [threshold, tr](NodeList::iterator const &from, NodeList::iterator const &end) {
|
|
|
// Build a path for the segment between the two interators and measure it's length
|
|
|
Geom::PathBuilder builder;
|
|
|
builder.moveTo(from->position());
|
|
|
int debug_count = 0;
|
|
|
for (auto next_node = from; next_node && next_node != end; next_node.advance()) {
|
|
|
build_segment(builder, &*next_node, &*next_node.next());
|
|
|
debug_count++;
|
|
|
}
|
|
|
builder.flush();
|
|
|
|
|
|
for (auto &path : builder.peek()) {
|
|
|
auto len = Geom::length(Geom::paths_to_pw(path) * tr);
|
|
|
std::cout << "Segments(" << debug_count << ") length: " << len << " <= " << threshold << "\n";
|
|
|
return len <= threshold;
|
|
|
}
|
|
|
return true;
|
|
|
};
|
|
|
|
|
|
if (_selection.size() < 2)
|
|
|
return;
|
|
|
hideDragPoint();
|
|
|
|
|
|
for (auto sp : _subpaths) {
|
|
|
// clang-format off
|
|
|
// ========== DEBUG ============= //
|
|
|
auto index = [sp](NodeList::iterator const &n) {
|
|
|
int ret = 0;
|
|
|
for (auto n2 = sp->begin(); n2 && n2 != n; n2++) {
|
|
|
ret++;
|
|
|
}
|
|
|
return ret;
|
|
|
};
|
|
|
auto dout = [sp,index](NodeList::iterator const &n) {
|
|
|
std::cout << " " << n->front()->position() << " "
|
|
|
<< "o---[" << n->position() << "(" << index(n) << ")"
|
|
|
<< "]---o " << n->back()->position() << "\n";
|
|
|
};
|
|
|
// =========== DEBUG ========== //
|
|
|
// clang-format on
|
|
|
|
|
|
NodeList::iterator begin = sp->begin();
|
|
|
|
|
|
if (sp->closed()) {
|
|
|
std::cout << "Closed shape\n";
|
|
|
// Find a good starting point by skipping the first node IF it would
|
|
|
// join with the end of the path as a tiny segment (and thus be removed)
|
|
|
while (begin && begin->selected() && (begin.prev() && is_small(begin.prev(), begin))) {
|
|
|
std::cout << " - Advancing closed shape for better fit\n";
|
|
|
begin++;
|
|
|
}
|
|
|
} else {
|
|
|
std::cout << "Open shape\n";
|
|
|
}
|
|
|
|
|
|
while (begin) {
|
|
|
std::cout << " - Do work at (" << index(begin) << ")\n";
|
|
|
|
|
|
NodeList::iterator start;
|
|
|
NodeList::iterator end;
|
|
|
Geom::OptRect area;
|
|
|
|
|
|
for (auto node = begin; node != begin.prev() && node->selected() && node.next(); node.advance()) {
|
|
|
bool valid = node.next()->selected() && is_small(node, node.next());
|
|
|
|
|
|
if (valid && !start) {
|
|
|
std::cout << " + Next node is valid (" << index(node) << "); START\n";
|
|
|
// The use of OptRect here is needed because there isn't a "Point List" Geom object
|
|
|
// and initalising the Geom::Rect above would cause it to include 0,0
|
|
|
area = Geom::Rect(node->position(), node->position());
|
|
|
start = node;
|
|
|
} else if (start) {
|
|
|
area->expandTo(node->position());
|
|
|
if (valid) {
|
|
|
std::cout << " - Next node is valid (" << index(node) << "); CONT\n";
|
|
|
} else {
|
|
|
std::cout << " + Next node is INVALID (" << index(node) << "); END\n";
|
|
|
end = node;
|
|
|
break;
|
|
|
}
|
|
|
} else {
|
|
|
std::cout << " - Next node is INVALID (" << index(node) << "); SKIP\n";
|
|
|
}
|
|
|
}
|
|
|
|
|
|
std::cout << "\n";
|
|
|
|
|
|
if (start && end) {
|
|
|
std::cout << " == JOIN (" << index(start) << " - " << index(end) << ")\n";
|
|
|
auto new_pos = area->midpoint();
|
|
|
|
|
|
start->setType(NODE_CUSP, false);
|
|
|
start->setPosition(new_pos);
|
|
|
std::cout << " Node (" << index(start) << ") moved to " << new_pos << "\n";
|
|
|
|
|
|
// do not move handles if they aren't degenerate
|
|
|
if (!end->front()->isDegenerate()) {
|
|
|
// This is the exit handle for the bezier, we move it very slightly
|
|
|
// by the same amount as the delta between the sel_end and the new_pos
|
|
|
start->front()->setPosition(end->front()->position() - (end->position() - new_pos));
|
|
|
std::cout << " Exit (" << index(end) << ") moved to "
|
|
|
<< (end->front()->position() - (end->position() - new_pos)) << "\n";
|
|
|
} else {
|
|
|
std::cout << " No setting curve output\n";
|
|
|
}
|
|
|
|
|
|
begin = end.next();
|
|
|
for (auto to_delete = end; to_delete != start; to_delete = to_delete.prev()) {
|
|
|
std::cout << "Deleting node " << index(to_delete) << "\n";
|
|
|
sp->erase(to_delete);
|
|
|
}
|
|
|
} else {
|
|
|
begin++;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/** Replace contiguous selections of nodes in each subpath with one node. */
|
|
|
void PathManipulator::weldNodes(NodeList::iterator preserve_pos)
|
|
|
{
|
|
|