diff --git a/testsuite/gsk/meson.build b/testsuite/gsk/meson.build index 5f57daec41..a73c89ec8a 100644 --- a/testsuite/gsk/meson.build +++ b/testsuite/gsk/meson.build @@ -359,6 +359,7 @@ tests = [ ['path'], ['path-special-cases'], ['path-stroke'], + ['path-ops'], ['transform'], ['shader'], ] diff --git a/testsuite/gsk/path-ops.c b/testsuite/gsk/path-ops.c new file mode 100644 index 0000000000..b47ae128bf --- /dev/null +++ b/testsuite/gsk/path-ops.c @@ -0,0 +1,353 @@ +/* + * 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 . + * + * Authors: Matthias Clasen + */ + +#include + +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 " + "C 116.65585327148438 133.31172180175781, 133.31172180175781 149.97837829589844, 149.96757507324219 149.99998474121094 " + "C 166.64505004882812 150.0216064453125, 183.32252502441406 133.35494995117188, 200 100 " + "L 100 100 " + "Z " + "M 149.96755981445312 149.99998474121094 " + "C 133.31172180175781 150.02162170410156, 116.65585327148438 166.68827819824219, 100 200 " + "L 200 200 " + "C 183.32252502441406 166.64505004882812, 166.64505004882812 149.9783935546875, 149.96757507324219 150.00001525878906 " + "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 " + "C 109.21287536621094 118.42575073242188, 118.42575073242188 131.75888061523438, 127.63862609863281 139.9993896484375 " + "C 118.42625427246094 148.24038696289062, 109.21312713623047 161.57374572753906, 100 180 " + "L 200 180 " + "C 190.78688049316406 161.57374572753906, 181.57374572753906 148.24038696289062, 172.36061096191406 139.99993896484375 " + "C 181.57373046875 131.75961303710938, 190.786865234375 118.42626190185547, 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.63862609863281 139.9993896484375 " + "C 142.54594421386719 153.33332824707031, 157.45327758789062 153.33355712890625, 172.360595703125 140.00006103515625 " + "C 157.45353698730469 126.66668701171875, 142.54646301269531 126.66668701171875, 127.63938903808594 139.99993896484375 " + "Z" }, + { "M 100 100 Q 150 200 200 100 Z", + "M 100 180 Q 150 80 200 180 Z", + OP_DIFFERENCE, + "M 100 100 " + "C 109.21287536621094 118.42575073242188, 118.42575073242188 131.75888061523438, 127.63862609863281 139.9993896484375 " + "C 142.54646301269531 126.66668701171875, 157.45353698730469 126.66668701171875, 172.36061096191406 139.99993896484375 " + "C 181.57373046875 131.75961303710938, 190.786865234375 118.42626190185547, 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 " + "C 109.21287536621094 118.42575073242188, 118.42575073242188 131.75888061523438, 127.63862609863281 139.9993896484375 " + "C 142.54646301269531 126.66668701171875, 157.45353698730469 126.66668701171875, 172.36061096191406 139.99993896484375 " + "C 181.57373046875 131.75961303710938, 190.786865234375 118.42626190185547, 200 100 " + "L 100 100 Z " + "M 172.36062622070312 140.00006103515625 " + "C 157.45327758789062 153.33355712890625, 142.54594421386719 153.33332824707031, 127.63862609863281 139.9993896484375 " + "C 118.42625427246094 148.24038696289062, 109.21312713623047 161.57374572753906, 100 180 " + "L 200 180 " + "C 190.78688049316406 161.57374572753906, 181.57374572753906 148.24038696289062, 172.36061096191406 139.99993896484375 " + "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 " + "C 109.21287536621094 118.42575073242188, 118.42575073242188 131.75888061523438, 127.63862609863281 139.9993896484375 " + "C 118.42626190185547 148.24037170410156, 109.21312713623047 161.57373046875, 100 180 " + "L 200 180 " + "C 190.78712463378906 161.57424926757812, 181.57424926757812 148.24111938476562, 172.36137390136719 140.00062561035156 " + "C 181.57373046875 131.75961303710938, 190.786865234375 118.42626190185547, 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.63862609863281 139.9993896484375 " + "C 142.54594421386719 153.33332824707031, 157.45327758789062 153.33355712890625, 172.360595703125 140.00006103515625 " + "C 157.45405578613281 126.66668701171875, 142.54672241210938 126.66645812988281, 127.63939666748047 139.99992370605469 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 " + "C 109.21287536621094 118.42575073242188, 118.42575073242188 131.75888061523438, 127.63862609863281 139.9993896484375 " + "C 142.54672241210938 126.66645812988281, 157.45405578613281 126.66668701171875, 172.36137390136719 140.00062561035156 " + "C 181.57373046875 131.75961303710938, 190.786865234375 118.42626190185547, 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 " + "C 109.21287536621094 118.42575073242188, 118.42575073242188 131.75888061523438, 127.63862609863281 139.9993896484375 " + "C 142.54672241210938 126.66645812988281, 157.45405578613281 126.66668701171875, 172.36137390136719 140.00062561035156 " + "C 181.57373046875 131.75961303710938, 190.786865234375 118.42626190185547, 200 100 " + "L 100 100 Z " + "M 172.36062622070312 140.00006103515625 " + "C 157.45327758789062 153.33355712890625, 142.54594421386719 153.33332824707031, 127.63862609863281 139.9993896484375 " + "C 118.42626190185547 148.24037170410156, 109.21312713623047 161.57373046875, 100 180 " + "L 200 180 " + "C 190.78712463378906 161.57424926757812, 181.57424926757812 148.24111938476562, 172.36137390136719 140.00062561035156 " + "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.33332824707031 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.33332824707031 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 intersection */ + { "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.35 300 233.35 200 0 100 Z", + OP_UNION, + "M 175 100 L 175 248.73167419433594 L 175 249.94822692871094 L 175 251.26628112792969 " + "L 175 400 L 300 400 L 300 100 L 175 100 Z " + "M 174.9906005859375 248.32115173339844 " + "C 174.437255859375 198.88076782226562, 149.44038391113281 149.44038391113281, 100 100 " + "L 0 100 " + "C 115.68845367431641 149.57722473144531, 174.02178955078125 199.15444946289062, 175 248.73167419433594 " + "Z " + "M 100 400 " + "C 149.44171142578125 350.55828857421875, 174.4385986328125 301.11660766601562, 174.99066162109375 251.67486572265625 " + "C 174.02340698242188 300.84420776367188, 115.69005584716797 350.42208862304688, 0 400 " + "L 100 400 " + "Z" }, + }; + + for (int i = 0; i < G_N_ELEMENTS (tests); i++) + { + GskPath *p1, *p2, *p; + char *s; + + if (g_test_verbose ()) + g_test_message ("testcase %d op %d", i, tests[i].op); + + 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); + s = gsk_path_to_string (p); + g_assert_cmpstr (s, ==, tests[i].out); + + g_free (s); + gsk_path_unref (p); + gsk_path_unref (p1); + gsk_path_unref (p2); + } +} + +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 (); +}