Skip to content

Path operations

Scott Pakin edited this page Dec 25, 2023 · 9 revisions

The operations described on this page relate specifically to path objects. See also Transforming a path and Live Path Effects.

Converting shapes to paths

The following method on all shape objects is the Simple Inkscape Scripting equivalent of Inkscape's PathObject to Path menu option:

Method: to_path(all_curves)

all_curves is an optional Boolean that defaults to False. When True, the generated path will comprise exclusively cubic Bézier curves as opposed to lines, arcs, and quadratic Bézier curves. Move and closepath commands will still be present.

Example:

circle((100, 100), 25, stroke='brown', stroke_width=5)

to-path

The above is an ordinary circle. The generated SVG code represents it with a <circle>:

<circle
  cx="100" cy="100" r="25"
  style="stroke:brown;fill:none;stroke-width:5" />

We can convert the circle to a path with the to_path method:

Example:

circle((100, 100), 25, stroke='brown', stroke_width=5).to_path()

Now, the circle is represented with an SVG <path> instead of an SVG <circle>:

<path
  d="M 100.0 75.0 a 25.0 25.0 0 1 0 25.0 25.0 a 25.0 25.0 0 0 0 -25.0 -25.0 z"
  style="stroke:brown;fill:none;stroke-width:5" id="simp-ink-scr-173264-2" />

Note the use of the a (relative-movement elliptical arc curve) path command in the preceding SVG. Finally, we pass to_path its optional all_curves argument:

Example:

circle((100, 100), 25, stroke='brown', stroke_width=5).to_path(all_curves=True)

This time, the generated SVG <path> comprises primarily C (cubic Bézier curve) path commands:

<path
  d="M 100 75 C 89.8884 75 80.7725 81.0911 76.903 90.4329 C 73.0335 99.7748 75.1724 110.528 82.3223 117.678 C 89.4723 124.828 100.225 126.967 109.567 123.097 C 118.909 119.227 125 110.112 125 100 C 125 93.3696 122.366 87.0107 117.678 82.3223 C 112.989 77.6339 106.63 75 100 75 Z"
  style="stroke:brown;fill:none;stroke-width:5" />

At the time of this writing (Inkscape 1.3.2 and inkex 1.3.1), applying to_path to a text object is extremely slow. (It requires spawning two child copies of Inkscape.) Applying to_path to a text object does not work correctly in versions of Inkscape prior to 1.2.

Combining paths

Paths can be combined by invoking a path's append method:

Method: append(obj)

In that call, obj is either another path object or a list of path objects. Once a path object is appended it is removed from the list of objects to render in the image. append returns the modified path object.

append is most commonly used with the even-odd fill rule to cut holes in objects.

Example:

c1 = circle((50, 50), 50, fill='#005500', fill_rule='evenodd').to_path()
c2 = circle((100, 50), 50).to_path()
c1.append(c2)

path-append-1

An alternative to using the even-odd fill rule is to draw outer paths in one direction (e.g., clockwise) and inner paths (holes) in the opposite direction (e.g., counterclockwise). If a path was drawn the wrong direction it can be reversed with

Method: reverse()

which reverses the path represented by a path object. reverse returns the modified path object.

Example:

box = rect((0, 0), (200, 100), fill='#d4aa00').to_path()
hole1 = rect((25, 25), (75, 75)).to_path()
hole2 = rect((125, 25), (175, 75)).to_path()
box.append([hole1.reverse(), hole2.reverse()])

path-append-2

Applying path operations

Simple Inkscape Scripting can automate the application of Boolean path operations. These appear in the Inkscape GUI as PathUnion, PathIntersection, PathExclusion, etc.

Function: apply_path_operation(op, [obj, …])

where op is the operation to perform and [obj, …] is a list of path objects to which to apply the operation. apply_path_operation returns a list of objects that were created or modified by the operation.

Caveat 1: The original objects in the [obj, …]) list are invalidated by apply_path_operation and subsequently should not be used.

Caveat 2: apply_path_operation is implemented by spawning a child copy of Inkscape that performs the requested path operations. The function therefore can be rather slow to execute.

The set of available path operations can be determined by running

inkscape --action-list

from the command line and searching for actions that begin with path-. Remove the path- and use the rest of the string as the op argument to apply_path_operation. At the time of this writing, the following operations are available:

  • break-apart
  • combine
  • cut
  • difference
  • division
  • exclusion
  • fill-between-paths
  • fracture
  • intersection
  • simplify
  • split
  • union

Caveat 3: Pre-1.2 versions of Inkscape require verbs rather than actions for these operations. Run inkscape --verb-list and search for verbs that begin with Selection. Remove the Selection and use the rest of the string as the operation name. For example, Union, Intersect, and SymDiff are the pre-1.2 equivalents of union, intersection, and exclusion.

For example, consider a pair of overlapping circles:

Example:

c1 = circle((60, 60), 50, fill='magenta', stroke_width=4).to_path()
c2 = circle((90, 60), 50, fill='cyan', stroke_width=4).to_path()

path-difference-input

In the following code, apply_path_operation is used to subtract the cyan circle from the magenta circle. The result is a magenta crescent. The code then recolors the crescent yellow.

Example:

c1 = circle((60, 60), 50, fill='magenta', stroke_width=4).to_path()
c2 = circle((90, 60), 50, fill='cyan', stroke_width=4).to_path()
objs = apply_path_operation('difference', [c1, c2])
for o in objs:
    o.style(fill='yellow')

path-difference

As indicated by Caveat 1 above, the original objects, c1 and c2, are invalid after apply_path_operation completes and should no longer be used.

For convenience, Simple Inkscape Scripting provides operator overloads for certain path operations:

Operation Operator Return type
Union + Path
Difference - Path
Intersection * Path
Exclusion ^ Path
Division / List of paths
Cut path // List of paths

These operators correspond to the keyboard shortcuts in Inkscape's Path menu:

path-menu

Each operator takes two paths as arguments and performs the following steps:

  1. Move the path specified by the right argument into the same group as the path specified by the left argument—or to the top level if the left path does not lie within a group.
  2. Raise the right path to the top of the Z-order.
  3. Perform the specified path operation.
  4. Return either a single path or a list of paths, as appropriate.

(The first two steps ensure that the outcome matches the corresponding path operation performed manually in the Inkscape GUI.)

Because the overloaded operators are implemented in terms of apply_path_operation, the same caveats apply to them. Furthermore, the operators fail silently when used in pre-1.2 versions of Inkscape.

To demonstrate overloaded-operator usage, the yellow-crescent example above can be simplified to

Example:

c1 = circle((60, 60), 50, fill='magenta', stroke_width=4).to_path()
c2 = circle((90, 60), 50, fill='cyan', stroke_width=4).to_path()
moon = c1 - c2
moon.style(fill='yellow')

path-difference

Clone this wiki locally