Compare commits

...

5 Commits

Author SHA1 Message Date
Matthias Clasen
08b786a984 tools: Add more commands to gtk4-path-tool
Add pathops.
2023-09-23 20:15:33 -04:00
Matthias Clasen
54bf63259c gtk-demo: Add a demo for path ops
This demo is called Glyphs, since that is what
it works with.
2023-09-23 20:15:33 -04:00
Matthias Clasen
53b5441e39 Add some tests for pathops 2023-09-23 20:15:33 -04:00
Matthias Clasen
3b2ab7ad1e Add API for boolean operations on paths
The new APIs here are:
gsk_path_union
gsk_path_intersection
gsk_path_difference
gsk_path_symmetric_difference
gsk_path_simplify
2023-09-23 20:15:33 -04:00
Matthias Clasen
42eaacbab4 Implement boolean operations on paths
Implement union, intersection, difference and
symmetric difference of two paths, as well as
simplification of a single path.
2023-09-23 20:15:33 -04:00
14 changed files with 3570 additions and 0 deletions

View File

@@ -295,6 +295,7 @@
<file>gears.c</file>
<file>gestures.c</file>
<file>glarea.c</file>
<file>glyphs.c</file>
<file>gltransition.c</file>
<file>headerbar.c</file>
<file>hypertext.c</file>

1186
demos/gtk-demo/glyphs.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -34,6 +34,7 @@ demos = files([
'gestures.c',
'glarea.c',
'gltransition.c',
'glyphs.c',
'headerbar.c',
'hypertext.c',
'iconscroll.c',

View File

@@ -12,6 +12,11 @@ SYNOPSIS
--------
| **gtk4-path-tool** <COMMAND> [OPTIONS...] <PATH>
|
| **gtk4-path-tool** simplify [OPTIONS...] <PATH>
| **gtk4-path-tool** intersection [OPTIONS...] <PATH> <PATH>
| **gtk4-path-tool** union [OPTIONS...] <PATH> <PATH>
| **gtk4-path-tool** difference [OPTIONS...] <PATH> <PATH>
| **gtk4-path-tool** symmetric-difference [OPTIONS...] <PATH> <PATH>
| **gtk4-path-tool** decompose [OPTIONS...] <PATH>
| **gtk4-path-tool** show [OPTIONS...] <PATH>
| **gtk4-path-tool** render [OPTIONS...] <PATH>
@@ -195,6 +200,7 @@ The interior of the path is filled.
The limit at which to clip miters at line joins. The default value is 4.
<<<<<<< HEAD
``--dashes=VALUE``
The dash pattern to use for this stroke. A dash pattern is specified by
@@ -208,6 +214,21 @@ The interior of the path is filled.
The offset into the dash pattern where dashing should begin.
The default value is 0.
Boolean Operations
^^^^^^^^^^^^^^^^^^
The ``intersection``, ``union``, ``difference`` and ``symmetric-difference`` commands
perform boolean operations on paths. Given two paths, they create a new path which
encircles the area that is the intersection, union, difference or symmetric difference
of the areas encircled by the paths.
Simplification
^^^^^^^^^^^^^^
The ``simplify`` command removes areas of overlap from a path such that the resulting
path encircles the same area, but every edge in the resulting path is a boundary between
the inside and the outside.
Reversing
^^^^^^^^^

View File

@@ -193,6 +193,30 @@ gboolean gsk_path_foreach_intersection (GskPath
GskPathIntersectionFunc func,
gpointer user_data);
GDK_AVAILABLE_IN_4_14
GskPath * gsk_path_union (GskPath *first,
GskPath *second,
GskFillRule fill_rule);
GDK_AVAILABLE_IN_4_14
GskPath * gsk_path_intersection (GskPath *first,
GskPath *second,
GskFillRule fill_rule);
GDK_AVAILABLE_IN_4_14
GskPath * gsk_path_difference (GskPath *first,
GskPath *second,
GskFillRule fill_rule);
GDK_AVAILABLE_IN_4_14
GskPath * gsk_path_symmetric_difference (GskPath *first,
GskPath *second,
GskFillRule fill_rule);
GDK_AVAILABLE_IN_4_14
GskPath * gsk_path_simplify (GskPath *self,
GskFillRule fill_rule);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPath, gsk_path_unref)
G_END_DECLS

1818
gsk/gskpathops.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -56,5 +56,19 @@ gboolean gsk_path_foreach_with_tolerance (GskPath
void gsk_path_builder_add_contour (GskPathBuilder *builder,
GskContour *contour);
typedef enum
{
GSK_PATH_OP_SIMPLIFY,
GSK_PATH_OP_UNION,
GSK_PATH_OP_INTERSECTION,
GSK_PATH_OP_DIFFERENCE,
GSK_PATH_OP_XOR
} GskPathOp;
GskPath * gsk_path_op (GskPathOp operation,
GskFillRule fill_rule,
GskPath *first,
GskPath *second);
G_END_DECLS

View File

@@ -30,6 +30,7 @@ gsk_public_sources = files([
'gskpathbuilder.c',
'gskpathintersect.c',
'gskpathmeasure.c',
'gskpathops.c',
'gskpathparser.c',
'gskpathpoint.c',
'gskrenderer.c',

View File

@@ -375,6 +375,7 @@ tests = [
['path', [ 'path-utils.c' ] ],
['path-special-cases'],
['path-intersect'],
['path-ops', [ 'path-utils.c' ] ],
]
test_cargs = []

381
testsuite/gsk/path-ops.c Normal file
View File

@@ -0,0 +1,381 @@
/*
* Copyright © 2022 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
#include <gtk/gtk.h>
#include "path-utils.h"
typedef enum
{
OP_UNION,
OP_INTERSECTION,
OP_DIFFERENCE,
OP_SYMMETRIC_DIFFERENCE,
} Op;
static void
test_ops_simple (void)
{
struct {
const char *in1;
const char *in2;
Op op;
const char *out;
} tests[] = {
/* partially overlapping edge */
{ "M 100 100 L 100 200 L 200 200 Z",
"M 150 150 L 150 250 L 250 250 Z",
OP_UNION,
"M 100 100 L 100 200 L 150 200 L 150 250 L 250 250 L 200 200 L 150 150 L 100 100 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 150 150 L 150 250 L 250 250 Z",
OP_INTERSECTION,
"M 150 200 L 200 200 L 150 150 L 150 200 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 150 150 L 150 250 L 250 250 Z",
OP_DIFFERENCE,
"M 100 100 L 100 200 L 150 200 L 150 150 L 100 100 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 150 150 L 150 250 L 250 250 Z",
OP_SYMMETRIC_DIFFERENCE,
"M 100 100 L 100 200 L 150 200 L 150 150 L 100 100 Z M 200 200 L 150 200 L 150 250 "
"L 250 250 L 200 200 Z" },
/* two triangles in general position */
{ "M 100 100 L 100 200 L 200 200 Z",
"M 170 120 L 100 240 L 170 240 Z",
OP_UNION,
"M 100 100 L 100 200 L 123.33333587646484 200 L 100 240 L 170 240 L 170 200 L 200 200 "
"L 170 170 L 170 120 L 151.57894897460938 151.57894897460938 L 100 100 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 170 120 L 100 240 L 170 240 Z",
OP_INTERSECTION,
"M 123.33333587646484 200 L 170 200 L 170 170 L 151.57894897460938 151.57894897460938 "
"L 123.33332824707031 200 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 170 120 L 100 240 L 170 240 Z",
OP_DIFFERENCE,
"M 100 100 L 100 200 L 123.33333587646484 200 L 151.57894897460938 151.57894897460938 "
"L 100 100 Z M 170 200 L 200 200 L 170 170 L 170 200 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 170 120 L 100 240 L 170 240 Z",
OP_SYMMETRIC_DIFFERENCE,
"M 100 100 L 100 200 L 123.33333587646484 200 L 151.57894897460938 151.57894897460938 "
"L 100 100 Z M 170 200 L 123.33333587646484 200 L 100 240 L 170 240 L 170 200 Z "
"M 170 200 L 200 200 L 170 170 L 170 200 Z M 151.57894897460938 151.57894897460938 "
"L 170 170 L 170 120 L 151.57894897460938 151.57894897460938 Z" },
/* nested contours, oriented in opposite direction */
{ "M 100 100 L 100 200 L 200 200 Z",
"M 120 140 L 170 190 L 120 190 Z",
OP_UNION,
"M 100 100 L 100 200 L 200 200 L 100 100 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 120 140 L 170 190 L 120 190 Z",
OP_INTERSECTION,
"M 170 190 L 120 140 L 120 190 L 170 190 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 120 140 L 170 190 L 120 190 Z",
OP_DIFFERENCE,
"M 100 100 L 100 200 L 200 200 L 100 100 Z M 120 140 L 170 190 L 120 190 L 120 140 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 120 140 L 170 190 L 120 190 Z",
OP_SYMMETRIC_DIFFERENCE,
"M 100 100 L 100 200 L 200 200 L 100 100 Z M 120 140 L 170 190 L 120 190 L 120 140 Z" },
/* nested contours, oriented in opposite direction, other way around */
{ "M 100 100 L 200 200 L 100 200 Z",
"M 120 140 L 120 190 L 170 190 Z",
OP_UNION,
"M 200 200 L 100 100 L 100 200 L 200 200 Z" },
{ "M 100 100 L 200 200 L 100 200 Z",
"M 120 140 L 120 190 L 170 190 Z",
OP_INTERSECTION,
"M 120 140 L 120 190 L 170 190 L 120 140 Z" },
{ "M 100 100 L 200 200 L 100 200 Z",
"M 120 140 L 120 190 L 170 190 Z",
OP_DIFFERENCE,
"M 200 200 L 100 100 L 100 200 L 200 200 Z M 120 190 L 120 140 L 170 190 L 120 190 Z" },
{ "M 100 100 L 200 200 L 100 200 Z",
"M 120 140 L 120 190 L 170 190 Z",
OP_SYMMETRIC_DIFFERENCE,
"M 200 200 L 100 100 L 100 200 L 200 200 Z M 120 190 L 120 140 L 170 190 L 120 190 Z" },
/* nested contours, oriented in the same direction */
{ "M 100 100 L 100 200 L 200 200 Z",
"M 120 140 L 120 190 L 170 190 Z",
OP_UNION,
"M 100 100 L 100 200 L 200 200 L 100 100 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 120 140 L 120 190 L 170 190 Z",
OP_INTERSECTION,
"M 120 140 L 120 190 L 170 190 L 120 140 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 120 140 L 120 190 L 170 190 Z",
OP_DIFFERENCE,
"M 100 100 L 100 200 L 200 200 L 100 100 Z M 120 190 L 120 140 L 170 190 L 120 190 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 120 140 L 120 190 L 170 190 Z",
OP_SYMMETRIC_DIFFERENCE,
"M 100 100 L 100 200 L 200 200 L 100 100 Z M 120 190 L 120 140 L 170 190 L 120 190 Z" },
/* a 3-way intersection */
{ "M 100 200 L 150 104 L 145 104 L 200 200 Z",
"M 100 108.571 L 200 108.571 L 200 50 L 100 50 Z",
OP_UNION,
"M 147.61904907226562 108.57142639160156 L 100 200 L 200 200 "
"L 147.61904907226562 108.57142639160156 Z M 100 108.57099914550781 "
"L 147.61927795410156 108.57099914550781 L 200 108.57099914550781 L 200 50 "
"L 100 50 L 100 108.57099914550781 Z" },
{ "M 100 200 L 150 104 L 145 104 L 200 200 Z",
"M 100 108.571 L 200 108.571 L 200 50 L 100 50 Z",
OP_INTERSECTION,
"M 147.61904907226562 108.57142639160156 L 150 104 L 145 104 "
"L 147.61904907226562 108.57142639160156 Z" },
{ "M 100 200 L 150 104 L 145 104 L 200 200 Z",
"M 100 108.571 L 200 108.571 L 200 50 L 100 50 Z",
OP_DIFFERENCE,
"M 147.61904907226562 108.57142639160156 L 100 200 L 200 200 "
"L 147.61904907226562 108.57142639160156 Z" },
{ "M 100 200 L 150 104 L 145 104 L 200 200 Z",
"M 100 108.571 L 200 108.571 L 200 50 L 100 50 Z",
OP_SYMMETRIC_DIFFERENCE,
"M 147.61904907226562 108.57142639160156 L 100 200 L 200 200 "
"L 147.61904907226562 108.57142639160156 Z M 150 104 "
"L 147.61904907226562 108.57142639160156 L 200 108.57099914550781 "
"L 200 50 L 100 50 L 100 108.57099914550781 L 147.61927795410156 108.57099914550781 "
"L 145 104 L 150 104 Z" },
/* touching quadratics */
{ "M 100 100 Q 150 200 200 100 Z",
"M 100 200 Q 150 100 200 200 Z",
OP_UNION,
"M 100 100 "
"Q 124.987984 149.975967, 149.975967 149.999985 "
"Q 174.987976 150.024033, 200 100 "
"L 100 100 "
"Z "
"M 149.975967 150 "
"Q 124.987984 150.024033, 100 200 "
"L 200 200 "
"Q 174.987976 149.975967, 149.975967 150.000015 "
"Z" },
/* overlapping quadratics, two intersections, different orientations */
{ "M 100 100 Q 150 200 200 100 Z",
"M 100 180 Q 150 80 200 180 Z",
OP_UNION,
"M 100 100 "
"Q 113.819313 127.638626, 127.638626 139.999374 "
"Q 113.819695 152.360611, 100 180 "
"L 200 180 "
"Q 186.180313 152.360611, 172.360611 139.999939 "
"Q 186.180298 127.639389, 200 100 "
"L 100 100 "
"Z" },
{ "M 100 100 Q 150 200 200 100 Z",
"M 100 180 Q 150 80 200 180 Z",
OP_INTERSECTION,
"M 127.638626 139.99939 "
"Q 149.999619 160.000275, 172.360611 140.000061 "
"Q 150 120.000061, 127.639389 139.999939 "
"Z" },
{ "M 100 100 Q 150 200 200 100 Z",
"M 100 180 Q 150 80 200 180 Z",
OP_DIFFERENCE,
"M 100 100 "
"Q 113.819313 127.638626, 127.638626 139.999374 "
"Q 150 120.000061, 172.360611 139.999939 "
"Q 186.180298 127.639389, 200 100 "
"L 100 100 "
"Z" },
{ "M 100 100 Q 150 200 200 100 Z",
"M 100 180 Q 150 80 200 180 Z",
OP_SYMMETRIC_DIFFERENCE,
"M 100 100 "
"Q 113.819313 127.638626, 127.638626 139.999374 "
"Q 150 120.000061, 172.360611 139.999939 "
"Q 186.180298 127.639389, 200 100 "
"L 100 100 "
"Z "
"M 172.360611 140.000061 "
"Q 149.999619 160.000275, 127.638626 139.999374 "
"Q 113.819695 152.360611, 100 180 "
"L 200 180 "
"Q 186.180313 152.360611, 172.360611 139.999939 "
"Z" },
/* overlapping quadratics, two intersections, same orientation */
{ "M 100 100 Q 150 200 200 100 Z",
"M 100 180 L 200 180 Q 150 80 100 180 Z",
OP_UNION,
"M 100 100 "
"Q 113.819313 127.638626, 127.638626 139.999374 "
"Q 113.819695 152.360611, 100 180 "
"L 200 180 "
"Q 186.180695 152.361374, 172.361389 140.000626 "
"Q 186.180298 127.639389, 200 100 "
"L 100 100 "
"Z" },
{ "M 100 100 Q 150 200 200 100 Z",
"M 100 180 L 200 180 Q 150 80 100 180 Z",
OP_INTERSECTION,
"M 127.638626 139.99939 "
"Q 149.999619 160.000275, 172.360611 140.000061 "
"Q 150.000397 119.999725, 127.639397 139.999939 "
"Z" },
{ "M 100 100 Q 150 200 200 100 Z",
"M 100 180 L 200 180 Q 150 80 100 180 Z",
OP_DIFFERENCE,
"M 100 100 "
"Q 113.819313 127.638626, 127.638626 139.999374 "
"Q 150.000397 119.999725, 172.361389 140.000626 "
"Q 186.180298 127.639389, 200 100 "
"L 100 100 "
"Z" },
{ "M 100 100 Q 150 200 200 100 Z",
"M 100 180 L 200 180 Q 150 80 100 180 Z",
OP_SYMMETRIC_DIFFERENCE,
"M 100 100 "
"Q 113.819313 127.638626, 127.638626 139.999374 "
"Q 150.000397 119.999725, 172.361389 140.000626 "
"Q 186.180298 127.639389, 200 100 "
"L 100 100 "
"Z "
"M 172.360611 140.000061 "
"Q 149.999619 160.000275, 127.638626 139.999374 "
"Q 113.819695 152.360611, 100 180 "
"L 200 180 "
"Q 186.180695 152.361374, 172.361389 140.000626 "
"Z" },
/* two polygons with near edges */
{ "M 100 100 L 100 200 L 400 200 L 400 100 Z",
"M 150 103 L 250 100 L 300 103 L 250 180 Z",
OP_UNION,
"M 100 100 L 100 200 L 400 200 L 400 100 L 250 100 L 100 100 Z" },
{ "M 100 100 L 100 200 L 400 200 L 400 100 Z",
"M 150 103 L 250 100 L 300 103 L 250 180 Z",
OP_INTERSECTION,
"M 250 100 L 150 103 L 250 180 L 300 103 L 250 100 Z" },
{ "M 100 100 L 100 200 L 400 200 L 400 100 Z",
"M 150 103 L 250 100 L 300 103 L 250 180 Z",
OP_DIFFERENCE,
"M 100 100 L 100 200 L 400 200 L 400 100 L 250 100 L 300 103 L 250 180 L 150 103 L 250 100 L 100 100 Z" },
{ "M 100 100 L 100 200 L 400 200 L 400 100 Z",
"M 150 103 L 250 100 L 300 103 L 250 180 Z",
OP_SYMMETRIC_DIFFERENCE,
"M 100 100 L 100 200 L 400 200 L 400 100 L 250 100 L 300 103 L 250 180 L 150 103 L 250 100 L 100 100 Z" },
/* Collinear line segments */
{ "M 100 100 L 200 100 L 250 100 L 100 200 Z",
"M 150 100 L 300 100 L 300 200 Z",
OP_UNION,
"M 150 100 "
"L 100 100 "
"L 100 200 "
"L 200 133.333328 "
"L 300 200 "
"L 300 100 "
"L 250 100 "
"L 200 100 "
"L 150 100 "
"Z" },
{ "M 100 100 L 200 100 L 250 100 L 100 200 Z",
"M 150 100 L 300 100 L 300 200 Z",
OP_INTERSECTION,
"M 200 100 "
"L 150 100 "
"L 200 133.333328 "
"L 250 100 "
"L 200 100 "
"Z" },
{ "M 100 100 L 200 100 L 250 100 L 100 200 Z",
"M 150 100 L 300 100 L 300 200 Z",
OP_DIFFERENCE,
"M 150 100 L 100 100 L 100 200 L 200 133.33332824707031 L 150 100 Z" },
{ "M 100 100 L 200 100 L 250 100 L 100 200 Z",
"M 150 100 L 300 100 L 300 200 Z",
OP_SYMMETRIC_DIFFERENCE,
"M 150 100 L 100 100 L 100 200 L 200 133.33332824707031 L 150 100 Z "
"M 250 100 L 200 133.33332824707031 L 300 200 L 300 100 L 250 100 Z" },
/* a complicated union */
{ "M 175 100 L 175 400 L 300 400 L 300 100 z",
"M 100 100 C 200 200 200 300 100 400 L 0 400 C 233.3333334 300 233.3333334 200 0 100 Z",
OP_UNION,
"M 175 100 "
"L 175 250 "
"L 175 400 "
"L 300 400 "
"L 300 100 "
"L 175 100 "
"Z "
"M 175 250 "
"Q 175 175, 100 100 "
"L 0 100 "
"Q 174.955811 174.981064, 174.999985 249.962112 "
"Z "
"M 100 400 "
"Q 175 325, 175 250 "
"Q 175.044189 324.981049, 0 400 "
"L 100 400 "
"Z" },
};
for (int i = 0; i < G_N_ELEMENTS (tests); i++)
{
GskPath *p1, *p2, *p3, *p;
if (g_test_verbose ())
{
const char *opname[] = { "union", "intersection", "difference", "symmetric-difference" };
g_test_message ("testcase %d op %s \"%s\" \"%s\"", i, opname[tests[i].op], tests[i].in1, tests[i].in2);
}
p1 = gsk_path_parse (tests[i].in1);
p2 = gsk_path_parse (tests[i].in2);
switch (tests[i].op)
{
case OP_UNION:
p = gsk_path_union (p1, p2, GSK_FILL_RULE_WINDING);
break;
case OP_INTERSECTION:
p = gsk_path_intersection (p1, p2, GSK_FILL_RULE_WINDING);
break;
case OP_DIFFERENCE:
p = gsk_path_difference (p1, p2, GSK_FILL_RULE_WINDING);
break;
case OP_SYMMETRIC_DIFFERENCE:
p = gsk_path_symmetric_difference (p1, p2, GSK_FILL_RULE_WINDING);
break;
default:
g_assert_not_reached ();
}
g_assert_nonnull (p);
p3 = gsk_path_parse (tests[i].out);
assert_path_equal_with_epsilon (p, p3, 0.0001);
gsk_path_unref (p);
gsk_path_unref (p1);
gsk_path_unref (p2);
gsk_path_unref (p3);
}
}
int
main (int argc,
char *argv[])
{
gtk_test_init (&argc, &argv, NULL);
g_test_add_func ("/ops/simple", test_ops_simple);
return g_test_run ();
}

View File

@@ -0,0 +1,104 @@
/* Copyright 2023 Red Hat, Inc.
*
* GTK+ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* GLib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with GTK+; see the file COPYING. If not,
* see <http://www.gnu.org/licenses/>.
*
* Author: Matthias Clasen
*/
#include "config.h"
#include <gtk/gtk.h>
#include "gtk-path-tool.h"
#include <glib/gi18n-lib.h>
void
do_pathop (const char *op, int *argc, const char ***argv)
{
GError *error = NULL;
const char *fill = "winding";
char **args = NULL;
GOptionContext *context;
GOptionEntry entries[] = {
{ "fill-rule", 0, 0, G_OPTION_ARG_STRING, &fill, N_("Fill rule"), N_("RULE") },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args, NULL, N_("PATH…") },
{ NULL, },
};
GskPath *path1, *path2, *result;
GskFillRule fill_rule;
char *prgname;
char *summary;
prgname = g_strconcat ("gtk4-path-tool ", op, NULL);
summary = g_strdup_printf (_("Apply the %s path operation."), op);
g_set_prgname (prgname);
context = g_option_context_new (NULL);
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
g_option_context_add_main_entries (context, entries, NULL);
g_option_context_set_summary (context, summary);
if (!g_option_context_parse (context, argc, (char ***)argv, &error))
{
g_printerr ("%s\n", error->message);
g_error_free (error);
exit (1);
}
g_option_context_free (context);
if (args == NULL)
{
g_printerr ("%s\n", _("No paths given."));
exit (1);
}
path1 = get_path (args[0]);
if (args[1] != NULL)
path2 = get_path (args[1]);
else
path2 = NULL;
fill_rule = get_enum_value (GSK_TYPE_FILL_RULE, _("fill rule"), fill);
if (strcmp (op, "simplify") == 0)
result = gsk_path_simplify (path1, fill_rule);
else if (strcmp (op, "union") == 0)
result = gsk_path_union (path1, path2, fill_rule);
else if (strcmp (op, "intersection") == 0)
result = gsk_path_intersection (path1, path2, fill_rule);
else if (strcmp (op, "difference") == 0)
result = gsk_path_difference (path1, path2, fill_rule);
else if (strcmp (op, "symmetric-difference") == 0)
result = gsk_path_symmetric_difference (path1, path2, fill_rule);
else
{
char *msg = g_strdup_printf (_("'%s' is not a supported operation."), op);
g_printerr ("%s\n", msg);
exit (1);
}
if (result)
{
char *str = gsk_path_to_string (result);
g_print ("%s\n", str);
g_free (str);
}
else
{
g_printerr ("%s\n", _("That didn't work out."));
exit (1);
}
}

View File

@@ -38,6 +38,12 @@ usage (void)
"Perform various tasks on paths.\n"
"\n"
"Commands:\n"
" simplify Simplify the path\n"
" intersection Intersect two paths\n"
" union Create the union of two paths\n"
" difference Create the difference of two paths\n"
" symmetric-difference\n"
" Create the symmetric difference of two paths\n"
" decompose Decompose the path\n"
" reverse Reverse the path\n"
" restrict Restrict the path to a segment\n"
@@ -128,8 +134,12 @@ main (int argc, const char *argv[])
if (strcmp (argv[0], "decompose") == 0)
do_decompose (&argc, &argv);
else if (strcmp (argv[0], "difference") == 0)
do_pathop (argv[0], &argc, &argv);
else if (strcmp (argv[0], "info") == 0)
do_info (&argc, &argv);
else if (strcmp (argv[0], "intersection") == 0)
do_pathop (argv[0], &argc, &argv);
else if (strcmp (argv[0], "render") == 0)
do_render (&argc, &argv);
else if (strcmp (argv[0], "restrict") == 0)
@@ -138,6 +148,12 @@ main (int argc, const char *argv[])
do_reverse (&argc, &argv);
else if (strcmp (argv[0], "show") == 0)
do_show (&argc, &argv);
else if (strcmp (argv[0], "simplify") == 0)
do_pathop (argv[0], &argc, &argv);
else if (strcmp (argv[0], "symmetric-difference") == 0)
do_pathop (argv[0], &argc, &argv);
else if (strcmp (argv[0], "union") == 0)
do_pathop (argv[0], &argc, &argv);
else
usage ();

View File

@@ -6,6 +6,7 @@ void do_restrict (int *argc, const char ***argv);
void do_reverse (int *argc, const char ***argv);
void do_render (int *argc, const char ***argv);
void do_show (int *argc, const char ***argv);
void do_pathop (const char *op, int *argc, const char ***argv);
GskPath *get_path (const char *arg);
int get_enum_value (GType type,

View File

@@ -26,6 +26,7 @@ gtk_tools = [
['gtk4-path-tool', ['gtk-path-tool.c',
'gtk-path-tool-decompose.c',
'gtk-path-tool-info.c',
'gtk-path-tool-pathops.c',
'gtk-path-tool-render.c',
'gtk-path-tool-restrict.c',
'gtk-path-tool-reverse.c',