/*
7 * Created by fred on Fri Dec 05 2003.
8 * tweaked endlessly by bulia byak <buliabyak@users.sf.net>
10 * Copyright (C) 2018 Authors
11 * Released under GNU GPL v2+, read the file
'COPYING' for more information.
18#include <glibmm/i18n.h>
54 _pathBoolOp(
bool_op_inters, INKSCAPE_ICON(
"path-intersection"), _(
"Intersection"), skip_undo, silent);
59 _pathBoolOp(
bool_op_diff, INKSCAPE_ICON(
"path-difference"), _(
"Difference"), skip_undo, silent);
64 _pathBoolOp(
bool_op_symdiff, INKSCAPE_ICON(
"path-exclusion"), _(
"Exclusion"), skip_undo, silent);
69 _pathBoolOp(
bool_op_cut, INKSCAPE_ICON(
"path-division"), _(
"Division"), skip_undo, silent);
74 _pathBoolOp(
bool_op_slice, INKSCAPE_ICON(
"path-cut"), _(
"Cut path"), skip_undo, silent);
94 path.
Fill(&tmp, path_id,
false, close_if_needed);
95 result.ConvertToShape(&tmp, fill_rule);
121 return path.
pts.size() == 2 && path.
pts[0].isMoveTo && !path.
pts[1].isMoveTo;
124static inline void distribute_intersection_times(std::vector<Geom::PathVectorTime> &dst1, std::vector<Geom::PathVectorTime> &dst2, std::vector<Geom::PathVectorIntersection>
const &intersections)
126 auto filter_and_add = [] (
auto const &x,
auto &dst) {
132 for (
auto const &x : intersections) {
133 filter_and_add(x.first, dst1);
134 filter_and_add(x.second, dst2);
140 std::sort(begin(vec),
end(vec));
143 for (
auto it = vec.begin(); it != vec.end(); ) {
144 if (it->path_index == prev.path_index && it->curve_index == prev.curve_index && it->t < prev.t +
Geom::EPSILON) {
159 std::vector<Geom::PathVectorTime> times;
167 shape.ConvertToForme(&res, 1, std::begin({ &path }));
183 std::vector<Geom::PathVectorTime> timesa, timesb;
200 int *nesting =
nullptr;
201 int *conts =
nullptr;
202 shape.
ConvertToFormeNested(&path, 2, std::begin({ &patha, &pathb }), num_nesting, nesting, conts,
true);
207 std::vector<Geom::PathVector>
result;
208 result.reserve(num_paths);
210 for (
int i = 0; i < num_paths; i++) {
224 std::vector<Geom::PathVectorTime> timesa, timesb;
243 shape.
Booleen(&shapeb, &shapea, bop);
279 pathb.Fill(&tmp, 0,
false,
false,
false);
280 patha.Fill(&tmp, 1,
true,
false,
false);
285 std::vector<Path::cut_position> toCut;
301 if (shape.
ebData[cb].pathID == 0) {
303 piece = shape.
ebData[cb].pieceID;
307 if (shape.
ebData[cb].pathID == 1) {
313 if (nbOrig > 0 && nbOther > 0) {
317 toCut.push_back({ .piece = piece, .t = t });
324 if (shape.
ebData[i].pathID == 1) {
330 result.ConvertPositionsToMoveTo(toCut.size(), toCut.data());
333 return result.MakePathVector();
343 }
catch (
char const *
msg) {
348 g_printerr(
"%s\n",
msg);
356 auto const doc = document();
359 auto const il = std::vector<SPItem*>(
items().begin(),
items().
end());
365 throw _(
"Select <b>at least 1 path</b> to perform a boolean union.");
371 throw _(
"Select <b>at least 2 paths</b> to perform an intersection or symmetric difference.");
377 if (il.size() != 2) {
378 throw _(
"Select <b>exactly 2 paths</b> to perform difference, division, or path cut.");
387 bool reverseOrderForOp =
false;
402 reverseOrderForOp =
true;
420 reverseOrderForOp =
true;
431 for (
auto item : il) {
432 if (!is<SPShape>(
item) && !is<SPText>(
item) && !is<SPFlowtext>(
item)) {
444 std::vector<Geom::PathVectorTime> cuts;
445 std::unique_ptr<Path> path;
448 std::vector<Operand> operands;
449 operands.
resize(il.size());
452 for (
int i = 0; i < il.size(); i++) {
454 auto &operand = operands[i];
458 if (
auto lpeitem = cast<SPLPEItem>(
item)) {
460 lpeitem->removeAllPathEffects(
true);
462 if (elemref && elemref !=
item) {
465 item = cast<SPItem>(elemref);
486 for (
int i = 0; i < operands.size(); i++) {
487 for (
int j = 0; j < i; j++) {
492 for (
auto &operand : operands) {
496 operand.path = std::make_unique<Path>();
497 operand.path->LoadPathVector(operand.pathv, operand.cuts);
500 if (operand.path->descr_cmd.size() <= 1) {
507 if (reverseOrderForOp) {
508 std::swap(operands[0], operands[1]);
524 operands[0].path->Fill(theShape, 0);
528 for (
int i = 1; i < operands.size(); i++) {
529 operands[i].path->Fill(theShape, i);
552 if (zeroA || zeroB) {
559 std::swap(theShapeA, theShapeB);
564 theShape->
Booleen(theShapeB, theShapeA, bop);
565 std::swap(theShape, theShapeA);
569 std::swap(theShape, theShapeA);
583 std::swap(operands[0], operands[1]);
585 operands[0].path->Fill(theShape, 0);
589 operands[1].path->Fill(theShape, 1,
false,
is_line(*operands[1].path),
false);
604 std::swap(operands[0], operands[1]);
606 operands[0].path->Fill(theShapeA, 0,
false,
false,
false);
608 operands[1].path->Fill(theShapeA, 1,
true,
false,
false);
625 while ( cb >= 0 && cb < theShape->numberOfEdges() ) {
626 if ( theShape->
ebData[cb].pathID == 0 ) {
628 piece=theShape->
ebData[cb].pieceID;
630 t=theShape->
ebData[cb].tSt;
632 t=theShape->
ebData[cb].tEn;
636 if ( theShape->
ebData[cb].pathID == 1 ) nbOther++;
637 cb=theShape->
NextAt(i, cb);
639 if ( nbOrig > 0 && nbOther > 0 ) {
644 toCut[nbToCut].
piece=piece;
655 if ( theShape->
ebData[i].pathID == 1 ) {
664 auto get_path_arr = [&] {
665 std::vector<Path *>
result;
666 result.reserve(operands.size());
667 for (
auto &operand : operands) {
668 result.emplace_back(operand.path.get());
673 int* nesting=
nullptr;
678 res->
Copy(operands[0].path.get());
710 if (reverseOrderForOp) {
717 std::vector<Inkscape::XML::Node*> sorted(xmlNodes().begin(), xmlNodes().
end());
721 source = doc->getObjectByRepr(sorted.front());
726 auto item_source = cast<SPItem>(source);
737 if (l != item_source) {
743 auto const source2doc_inverse = i2doc.
inverse();
744 char const *
const old_transform_attibute = repr_source->
attribute(
"transform");
764 if ( conts ) free(conts);
765 if ( nesting ) free(nesting);
769 std::vector <Inkscape::XML::Node*> selection;
770 for (
int i=0;i<nbRP;i++) {
771 resPath[i]->
Transform(source2doc_inverse);
781 item_source->deleteObject(
false);
784 repr->
setAttribute(
"d", resPath[i]->svg_dump_path().c_str());
802 parent->addChildAtPos(repr, pos);
804 selection.push_back(repr);
809 setReprList(selection);
810 if ( resPath ) free(resPath);
821 item_source->deleteObject(
false);
827 parent->addChildAtPos(repr, pos);
Various utility functions.
TODO: insert short description here.
TODO: insert short description here.
3x3 matrix representing an affine transformation.
Affine inverse() const
Compute the inverse matrix.
std::vector< PathVectorIntersection > intersectSelf(Coord precision=EPSILON) const
Get all intersections of the path-vector with itself.
void resize(size_type n)
Change the number of paths.
std::vector< PVIntersection > intersect(PathVector const &other, Coord precision=EPSILON) const
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
MessageId flash(MessageType type, char const *message)
Temporarily pushes a message onto the stack.
void pathUnion(bool skip_undo=false, bool silent=false)
void pathCut(bool skip_undo=false, bool silent=false)
void pathDiff(bool skip_undo=false, bool silent=false)
void pathIntersect(bool skip_undo=false, bool silent=false)
void pathSymDiff(bool skip_undo=false, bool silent=false)
void _pathBoolOp(BooleanOp bop, char const *icon_name, char const *description, bool skip_undo, bool silent)
void pathSlice(bool skip_undo=false, bool silent=false)
Interface for refcounted XML nodes.
virtual Node * parent()=0
Get the parent of this node.
void setAttributeOrRemoveIfEmpty(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
Change an attribute of this node.
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
virtual Node * firstChild()=0
Get the first child of this node.
virtual unsigned position() const =0
Get the index of this node in parent's child order.
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
Path and its polyline approximation.
void Fill(Shape *dest, int pathID=-1, bool justAdd=false, bool closeIfNeeded=true, bool invert=false)
Fills the shape with the polyline approximation stored in this object.
std::string svg_dump_path() const
std::vector< path_lineto > pts
void Transform(const Geom::Affine &trans)
Apply a transformation on all path commands.
void LoadPathVector(Geom::PathVector const &pv, Geom::Affine const &tr, bool doTransformation)
Load a lib2geom Geom::PathVector in this path object.
Geom::PathVector MakePathVector() const
Create a lib2geom Geom::PathVector from this Path object.
void Copy(Path *who)
Clear all stored path commands, resets flags and imports path commands from the passed Path object.
Path ** SubPathsWithNesting(int &outNb, bool killNoSurf, int nbNest, int *nesting, int *conts)
void ConvertPositionsToMoveTo(int nbPos, cut_position *poss)
Path ** SubPaths(int &outNb, bool killNoSurf)
std::vector< PathDescr * > descr_cmd
Inkscape::MessageStack * messageStack() const
Typed SVG document implementation.
SPObject * getObjectById(std::string const &id) const
Geom::Affine i2doc_affine() const
Returns the accumulated transformation of the item and all its ancestors, including root's viewport.
SPObject is an abstract base class of all of the document nodes at the SVG document level.
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
char const * getAttribute(char const *name) const
T< SPAttr::FILL_RULE, SPIEnum< SPWindRule > > fill_rule
fill-rule: 0 nonzero, 1 evenodd
A class to store/manipulate directed graphs.
void ConvertToForme(Path *dest)
Extract contours from a directed graph.
int Booleen(Shape *a, Shape *b, BooleanOp mod, int cutPathID=-1)
std::vector< back_data > ebData
void ConvertToFormeNested(Path *dest, int nbP, Path *const *orig, int &nbNest, int *&nesting, int *&contStart, bool never_split=false)
int numberOfEdges() const
Returns number of edges.
int NextAt(int p, int b) const
int numberOfPoints() const
Returns number of points.
dg_arete const & getEdge(int n) const
Get an edge.
int ConvertToShape(Shape *a, FillRule directed=fill_nonZero, bool invert=false)
Using a given fill rule, find all intersections in the shape given, create a new intersection free sh...
dg_point const & getPoint(int n) const
Get a point.
bool hasBackData() const
Do we have back data?
std::shared_ptr< Css const > css
static char const *const parent
TODO: insert short description here.
bool is_line(SPObject *i)
constexpr Coord EPSILON
Default "acceptably small" value.
Macro for icon names used in Inkscape.
Raw stack of active status messages.
vector< vector< Point > > paths
static R & release(R &r)
Decrements the reference count of a anchored object.
void copy_object_properties(XML::Node *dest, XML::Node const *src)
Copy generic object properties, like:
void flatten(Geom::PathVector &pathv, FillRule fill_rule)
constexpr auto RELATIVE_THRESHOLD
static void sort_and_clean_intersection_times(std::vector< Geom::PathVectorTime > &vec)
static void distribute_intersection_times(std::vector< Geom::PathVectorTime > &dst1, std::vector< Geom::PathVectorTime > &dst2, std::vector< Geom::PathVectorIntersection > const &intersections)
Geom::PathVector sp_pathvector_boolop(Geom::PathVector const &pathva, Geom::PathVector const &pathvb, BooleanOp bop, FillRule fra, FillRule frb)
Perform a boolean operation on two pathvectors.
static Path make_path(Geom::PathVector const &pathv, std::vector< Geom::PathVectorTime > const &cuts)
Create a path with backdata from a pathvector, automatically estimating a suitable conversion thresho...
static Shape make_shape(Path &path, int path_id=-1, FillRule fill_rule=fill_nonZero, bool close_if_needed=true)
Create a flattened shape from a path.
Geom::PathVector flattened(Geom::PathVector const &pathv, FillRule fill_rule)
Flatten a pathvector according to the given fill rule.
std::vector< Geom::PathVector > pathvector_cut(Geom::PathVector const &pathv, Geom::PathVector const &lines)
Cut a pathvector along a collection of lines into several smaller pathvectors.
std::optional< SPCurve > curve_for_item(SPItem *item)
Gets an SPCurve from the SPItem.
SPCSSAttr * sp_repr_css_attr_new()
Creates an empty SPCSSAttr (a class for manipulating CSS style properties).
void sp_repr_css_change(Node *repr, SPCSSAttr *css, gchar const *attr)
Creates a new SPCSAttr with the values filled from a repr, merges in properties from the given SPCSAt...
void sp_repr_css_attr_unref(SPCSSAttr *css)
Unreferences an SPCSSAttr (will be garbage collected if no references remain).
void sp_repr_css_set_property(SPCSSAttr *css, gchar const *name, gchar const *value)
Set a style property to a new value (e.g.
bool is_descendant_of(Inkscape::XML::Node const *descendant, Inkscape::XML::Node const *ancestor)
Inkscape::XML::Node const * lowest_common_ancestor(Inkscape::XML::Node const *a, Inkscape::XML::Node const *b)
Inkscape::XML::Node const * find_containing_child(Inkscape::XML::Node const *descendant, Inkscape::XML::Node const *ancestor)
TODO: insert short description here.
bool sp_repr_compare_position_bool(Inkscape::XML::Node const *first, Inkscape::XML::Node const *second)
TODO: insert short description here.
Generalized time value in the path vector.
Interface for XML documents.
virtual Node * createElement(char const *name)=0
SPStyle - a style object for SPItem objects.
parse SVG path specifications