Remove the path-bool library (#3882)

This commit is contained in:
Dennis Kobert 2026-03-11 08:55:03 +01:00
parent 58aae4f87b
commit 90533e656f
No known key found for this signature in database
GPG Key ID: 5A4358CB9530F933
179 changed files with 58 additions and 6616 deletions

579
Cargo.lock generated
View File

@ -55,15 +55,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "aligned-vec"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b"
dependencies = [
"equator",
]
[[package]]
name = "allocator-api2"
version = "0.2.21"
@ -174,23 +165,6 @@ version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
[[package]]
name = "arbitrary"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "arg_enum_proc_macro"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.106",
]
[[package]]
name = "arrayref"
version = "0.3.9"
@ -232,7 +206,7 @@ dependencies = [
"enumflags2",
"futures-channel",
"futures-util",
"rand 0.9.2",
"rand",
"raw-window-handle",
"serde",
"serde_repr",
@ -408,29 +382,6 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "av1-grain"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8"
dependencies = [
"anyhow",
"arrayvec",
"log",
"nom",
"num-rational",
"v_frame",
]
[[package]]
name = "avif-serialize"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f"
dependencies = [
"arrayvec",
]
[[package]]
name = "aws-lc-rs"
version = "1.16.0"
@ -498,12 +449,6 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bit_field"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
[[package]]
name = "bitflags"
version = "1.3.2"
@ -519,12 +464,6 @@ dependencies = [
"serde_core",
]
[[package]]
name = "bitstream-io"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2"
[[package]]
name = "blending-nodes"
version = "0.1.0"
@ -606,12 +545,6 @@ dependencies = [
"tokio",
]
[[package]]
name = "built"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b"
[[package]]
name = "bumpalo"
version = "3.19.0"
@ -822,16 +755,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cfg-expr"
version = "0.15.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
dependencies = [
"smallvec",
"target-lexicon",
]
[[package]]
name = "cfg-if"
version = "1.0.3"
@ -1148,8 +1071,8 @@ dependencies = [
"parley",
"petgraph 0.7.1",
"polycool",
"rand 0.9.2",
"rand_chacha 0.9.0",
"rand",
"rand_chacha",
"rustc-hash 2.1.1",
"serde",
"serde_json",
@ -1208,7 +1131,7 @@ dependencies = [
"ciborium",
"clap",
"criterion-plot",
"itertools 0.13.0",
"itertools",
"num-traits",
"oorandom",
"plotters",
@ -1227,7 +1150,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b1bcc0dc7dfae599d84ad0b1a55f80cde8af3725da8313b528da95ef783e338"
dependencies = [
"cast",
"itertools 0.13.0",
"itertools",
]
[[package]]
@ -1650,26 +1573,6 @@ dependencies = [
"log",
]
[[package]]
name = "equator"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc"
dependencies = [
"equator-macro",
]
[[package]]
name = "equator-macro"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.106",
]
[[package]]
name = "equivalent"
version = "1.0.2"
@ -1732,21 +1635,6 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "exr"
version = "1.73.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0"
dependencies = [
"bit_field",
"half",
"lebe",
"miniz_oxide",
"rayon-core",
"smallvec",
"zune-inflate",
]
[[package]]
name = "fastnoise-lite"
version = "1.1.1"
@ -2148,16 +2036,6 @@ dependencies = [
"weezl",
]
[[package]]
name = "gif"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e"
dependencies = [
"color_quant",
"weezl",
]
[[package]]
name = "gimli"
version = "0.31.1"
@ -2186,12 +2064,6 @@ dependencies = [
"serde",
]
[[package]]
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "glow"
version = "0.16.0"
@ -2280,7 +2152,6 @@ dependencies = [
"iai-callgrind",
"js-sys",
"log",
"path-bool-nodes",
"pretty_assertions",
"raster-nodes",
"rendering",
@ -2439,7 +2310,7 @@ dependencies = [
"objc2-app-kit 0.3.2",
"objc2-foundation 0.3.2",
"open",
"rand 0.9.2",
"rand",
"rfd",
"ron",
"serde",
@ -2971,28 +2842,11 @@ dependencies = [
"bytemuck",
"byteorder-lite",
"color_quant",
"exr",
"gif 0.13.3",
"image-webp",
"gif",
"num-traits",
"png 0.17.16",
"qoi",
"ravif",
"rayon",
"rgb",
"tiff",
"zune-core 0.4.12",
"zune-jpeg 0.4.20",
]
[[package]]
name = "image-webp"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
dependencies = [
"byteorder-lite",
"quick-error",
"png",
"zune-core",
"zune-jpeg",
]
[[package]]
@ -3001,12 +2855,6 @@ version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09e54e57b4c48b40f7aec75635392b12b3421fa26fe8b4332e63138ed278459c"
[[package]]
name = "imgref"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408"
[[package]]
name = "include_dir"
version = "0.7.4"
@ -3078,17 +2926,6 @@ dependencies = [
"libc",
]
[[package]]
name = "interpolate_name"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.106",
]
[[package]]
name = "interpreted-executor"
version = "0.1.0"
@ -3104,7 +2941,6 @@ dependencies = [
"iai-callgrind",
"log",
"once_cell",
"path-bool-nodes",
"preprocessor",
"serde",
"wgpu-executor",
@ -3162,15 +2998,6 @@ version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.13.0"
@ -3242,12 +3069,6 @@ dependencies = [
"libc",
]
[[package]]
name = "jpeg-decoder"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07"
[[package]]
name = "js-sys"
version = "0.3.77"
@ -3323,12 +3144,6 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "lebe"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
[[package]]
name = "libbz2-rs-sys"
version = "0.2.2"
@ -3341,16 +3156,6 @@ version = "0.2.175"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
[[package]]
name = "libfuzzer-sys"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404"
dependencies = [
"arbitrary",
"cc",
]
[[package]]
name = "libloading"
version = "0.8.8"
@ -3441,15 +3246,6 @@ version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "loop9"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
dependencies = [
"imgref",
]
[[package]]
name = "lru-slab"
version = "0.1.2"
@ -3520,7 +3316,7 @@ dependencies = [
"log",
"math-parser",
"node-macro",
"rand 0.9.2",
"rand",
"vector-types",
]
@ -3546,16 +3342,6 @@ dependencies = [
"rawpointer",
]
[[package]]
name = "maybe-rayon"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
dependencies = [
"cfg-if",
"rayon",
]
[[package]]
name = "memchr"
version = "2.7.5"
@ -3601,12 +3387,6 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
@ -3642,7 +3422,7 @@ dependencies = [
"objc2-core-foundation",
"objc2-foundation 0.3.2",
"once_cell",
"png 0.17.16",
"png",
"thiserror 2.0.18",
"windows-sys 0.60.2",
]
@ -3786,22 +3566,6 @@ dependencies = [
"syn 2.0.106",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "noop_proc_macro"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
[[package]]
name = "notify"
version = "8.2.0"
@ -3835,16 +3599,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.4.6"
@ -3880,17 +3634,6 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
@ -4279,40 +4022,18 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "path-bool"
version = "0.1.0"
dependencies = [
"criterion",
"glam",
"glob",
"image",
"lyon_geom",
"regex",
"resvg",
"roots",
"rustc-hash 2.1.1",
"slotmap",
"smallvec",
"svg",
]
[[package]]
name = "path-bool-nodes"
version = "0.1.0"
dependencies = [
"core-types",
"dyn-any",
"glam",
"graphic-types",
"linesweeper",
"log",
"node-macro",
"serde",
"smallvec",
"tsify",
"vector-types",
"wasm-bindgen",
]
[[package]]
@ -4566,19 +4287,6 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "png"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
dependencies = [
"bitflags 2.11.0",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide",
]
[[package]]
name = "polling"
version = "3.10.0"
@ -4724,28 +4432,6 @@ name = "profiling"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
dependencies = [
"profiling-procmacros",
]
[[package]]
name = "profiling-procmacros"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
dependencies = [
"quote",
"syn 2.0.106",
]
[[package]]
name = "qoi"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
dependencies = [
"bytemuck",
]
[[package]]
name = "qrcodegen"
@ -4753,12 +4439,6 @@ version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4339fc7a1021c9c1621d87f5e3505f2805c8c105420ba2f2a4df86814590c142"
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quick-xml"
version = "0.37.5"
@ -4807,7 +4487,7 @@ dependencies = [
"bytes",
"getrandom 0.3.3",
"lru-slab",
"rand 0.9.2",
"rand",
"ring",
"rustc-hash 2.1.1",
"rustls",
@ -4848,35 +4528,14 @@ version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha 0.3.1",
"rand_core 0.6.4",
]
[[package]]
name = "rand"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.3",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core 0.6.4",
"rand_chacha",
"rand_core",
]
[[package]]
@ -4886,16 +4545,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core 0.9.3",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.16",
"rand_core",
]
[[package]]
@ -4930,8 +4580,8 @@ dependencies = [
"node-macro",
"num-traits",
"num_enum",
"rand 0.9.2",
"rand_chacha 0.9.0",
"rand",
"rand_chacha",
"raster-nodes-shaders",
"raster-types",
"serde",
@ -4977,56 +4627,6 @@ dependencies = [
"wgpu",
]
[[package]]
name = "rav1e"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9"
dependencies = [
"arbitrary",
"arg_enum_proc_macro",
"arrayvec",
"av1-grain",
"bitstream-io",
"built",
"cfg-if",
"interpolate_name",
"itertools 0.12.1",
"libc",
"libfuzzer-sys",
"log",
"maybe-rayon",
"new_debug_unreachable",
"noop_proc_macro",
"num-derive",
"num-traits",
"once_cell",
"paste",
"profiling",
"rand 0.8.5",
"rand_chacha 0.3.1",
"simd_helpers",
"system-deps",
"thiserror 1.0.69",
"v_frame",
"wasm-bindgen",
]
[[package]]
name = "ravif"
version = "0.11.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b"
dependencies = [
"avif-serialize",
"imgref",
"loop9",
"quick-error",
"rav1e",
"rayon",
"rgb",
]
[[package]]
name = "raw-string"
version = "0.3.5"
@ -5229,23 +4829,6 @@ dependencies = [
"web-sys",
]
[[package]]
name = "resvg"
version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9be183ad6a216aa96f33e4c8033b0988b8b3ea6fd2359d19af5bac4643fd8e81"
dependencies = [
"gif 0.14.1",
"image-webp",
"log",
"pico-args",
"rgb",
"svgtypes",
"tiny-skia 0.12.0",
"usvg",
"zune-jpeg 0.5.12",
]
[[package]]
name = "rfd"
version = "0.15.4"
@ -5270,15 +4853,6 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "rgb"
version = "0.8.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
dependencies = [
"bytemuck",
]
[[package]]
name = "ring"
version = "0.17.14"
@ -5307,12 +4881,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "roots"
version = "0.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "082f11ffa03bbef6c2c6ea6bea1acafaade2fd9050ae0234ab44a2153742b058"
[[package]]
name = "roxmltree"
version = "0.20.0"
@ -5561,7 +5129,7 @@ dependencies = [
"log",
"memmap2",
"smithay-client-toolkit",
"tiny-skia 0.11.4",
"tiny-skia",
]
[[package]]
@ -5807,15 +5375,6 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "simd_helpers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
dependencies = [
"quote",
]
[[package]]
name = "simplecss"
version = "0.2.2"
@ -6090,12 +5649,6 @@ version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "svg"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94afda9cd163c04f6bee8b4bf2501c91548deae308373c436f36aeff3cf3c4a3"
[[package]]
name = "svg_fmt"
version = "0.4.5"
@ -6186,19 +5739,6 @@ dependencies = [
"libc",
]
[[package]]
name = "system-deps"
version = "6.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
dependencies = [
"cfg-expr",
"heck",
"pkg-config",
"toml 0.8.23",
"version-compare",
]
[[package]]
name = "tar"
version = "0.4.44"
@ -6210,12 +5750,6 @@ dependencies = [
"xattr",
]
[[package]]
name = "target-lexicon"
version = "0.12.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]]
name = "tempfile"
version = "3.21.0"
@ -6327,17 +5861,6 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "tiff"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
dependencies = [
"flate2",
"jpeg-decoder",
"weezl",
]
[[package]]
name = "time"
version = "0.3.41"
@ -6383,21 +5906,6 @@ dependencies = [
"tiny-skia-path 0.11.4",
]
[[package]]
name = "tiny-skia"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47ffee5eaaf5527f630fb0e356b90ebdec84d5d18d937c5e440350f88c5a91ea"
dependencies = [
"arrayref",
"arrayvec",
"bytemuck",
"cfg-if",
"log",
"png 0.18.1",
"tiny-skia-path 0.12.0",
]
[[package]]
name = "tiny-skia-path"
version = "0.11.4"
@ -6672,7 +6180,7 @@ dependencies = [
"glam",
"graphic-types",
"node-macro",
"rand 0.9.2",
"rand",
"serde",
"vector-types",
]
@ -6915,17 +6423,6 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "v_frame"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2"
dependencies = [
"aligned-vec",
"num-traits",
"wasm-bindgen",
]
[[package]]
name = "valuable"
version = "0.1.1"
@ -6946,7 +6443,7 @@ dependencies = [
"log",
"node-macro",
"qrcodegen",
"rand 0.9.2",
"rand",
"repeat-nodes",
"rustc-hash 2.1.1",
"serde",
@ -6990,7 +6487,7 @@ dependencies = [
"futures-intrusive",
"log",
"peniko",
"png 0.17.16",
"png",
"skrifa 0.40.0",
"static_assertions",
"thiserror 2.0.18",
@ -7025,12 +6522,6 @@ dependencies = [
"vello_encoding",
]
[[package]]
name = "version-compare"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
[[package]]
name = "version_check"
version = "0.9.5"
@ -8446,37 +7937,13 @@ version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
[[package]]
name = "zune-core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9"
[[package]]
name = "zune-inflate"
version = "0.2.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
dependencies = [
"simd-adler32",
]
[[package]]
name = "zune-jpeg"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc1f7e205ce79eb2da3cd71c5f55f3589785cb7c79f6a03d1c8d1491bda5d089"
dependencies = [
"zune-core 0.4.12",
]
[[package]]
name = "zune-jpeg"
version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "410e9ecef634c709e3831c2cfdb8d9c32164fae1c67496d5b68fff728eec37fe"
dependencies = [
"zune-core 0.5.1",
"zune-core",
]
[[package]]

View File

@ -10,7 +10,6 @@ members = [
"editor",
"frontend/wasm",
"libraries/dyn-any",
"libraries/path-bool",
"libraries/math-parser",
"node-graph/libraries/*",
"node-graph/nodes/*",
@ -33,7 +32,6 @@ default-members = [
"editor",
"frontend/wasm",
"libraries/dyn-any",
"libraries/path-bool",
"libraries/math-parser",
"node-graph/graph-craft",
"node-graph/interpreted-executor",
@ -67,7 +65,6 @@ dyn-any = { path = "libraries/dyn-any", features = [
] }
preprocessor = { path = "node-graph/preprocessor" }
math-parser = { path = "libraries/math-parser" }
path-bool = { path = "libraries/path-bool" }
graphene-application-io = { path = "node-graph/libraries/application-io" }
core-types = { path = "node-graph/libraries/core-types" }
no-std-types = { path = "node-graph/libraries/no-std-types" }

View File

@ -3,7 +3,7 @@ use crate::messages::input_mapper::utility_types::macros::action_shortcut;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis, GroupFolderType};
use crate::messages::prelude::*;
use graphene_std::path_bool::BooleanOperation;
use graphene_std::vector::misc::BooleanOperation;
#[derive(Debug, Clone, Default, ExtractField)]
pub struct MenuBarMessageHandler {

View File

@ -29,7 +29,7 @@ use glam::{DAffine2, DVec2, IVec2};
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput, NodeNetwork, OldNodeNetwork};
use graphene_std::math::quad::Quad;
use graphene_std::path_bool::boolean_intersect;
use graphene_std::path_bool_nodes::boolean_intersect;
use graphene_std::raster::BlendMode;
use graphene_std::raster_types::Raster;
use graphene_std::render_node::wgpu_available;

View File

@ -76,7 +76,7 @@ pub enum GraphOperationMessage {
},
NewBooleanOperationLayer {
id: NodeId,
operation: graphene_std::path_bool::BooleanOperation,
operation: graphene_std::vector::misc::BooleanOperation,
parent: LayerNodeIdentifier,
insert_index: usize,
},

View File

@ -142,7 +142,7 @@ impl<'a> ModifyInputsContext<'a> {
LayerNodeIdentifier::new(new_id, self.network_interface)
}
pub fn insert_boolean_data(&mut self, operation: graphene_std::path_bool::BooleanOperation, layer: LayerNodeIdentifier) {
pub fn insert_boolean_data(&mut self, operation: graphene_std::vector::misc::BooleanOperation, layer: LayerNodeIdentifier) {
let boolean = resolve_network_node_type("Boolean Operation").expect("Boolean node does not exist").node_template_input_override([
Some(NodeInput::value(TaggedValue::Graphic(Default::default()), true)),
Some(NodeInput::value(TaggedValue::BooleanOperation(operation), false)),

View File

@ -1783,7 +1783,7 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
nodes: vec![
DocumentNode {
inputs: vec![NodeInput::import(concrete!(Table<Vector>), 0), NodeInput::import(concrete!(vector::style::Fill), 1)],
implementation: DocumentNodeImplementation::ProtoNode(path_bool::boolean_operation::IDENTIFIER),
implementation: DocumentNodeImplementation::ProtoNode(path_bool_nodes::boolean_operation::IDENTIFIER),
call_argument: generic!(T),
..Default::default()
},
@ -1802,7 +1802,7 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
}),
inputs: vec![
NodeInput::value(TaggedValue::Graphic(Default::default()), true),
NodeInput::value(TaggedValue::BooleanOperation(path_bool::BooleanOperation::Union), false),
NodeInput::value(TaggedValue::BooleanOperation(vector::misc::BooleanOperation::Union), false),
],
..Default::default()
},

View File

@ -16,7 +16,6 @@ use graph_craft::{Type, concrete};
use graphene_std::NodeInputDecleration;
use graphene_std::animation::RealTimeMode;
use graphene_std::extract_xy::XY;
use graphene_std::path_bool::BooleanOperation;
use graphene_std::raster::curve::Curve;
use graphene_std::raster::{
BlendMode, CellularDistanceFunction, CellularReturnType, Color, DomainWarpType, FractalType, LuminanceCalculation, NoiseType, RedGreenBlue, RedGreenBlueAlpha, RelativeAbsolute,
@ -26,6 +25,7 @@ use graphene_std::table::{Table, TableRow};
use graphene_std::text::{Font, TextAlign};
use graphene_std::transform::{Footprint, ReferencePoint, Transform};
use graphene_std::vector::QRCodeErrorCorrectionLevel;
use graphene_std::vector::misc::BooleanOperation;
use graphene_std::vector::misc::{ArcType, CentroidType, ExtrudeJoiningAlgorithm, GridType, MergeByDistanceAlgorithm, PointSpacingType, RowsOrColumns, SpiralType};
use graphene_std::vector::style::{Fill, FillChoice, FillType, GradientStops, GradientType, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin};

View File

@ -710,5 +710,5 @@ impl PTZ {
#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum GroupFolderType {
Layer,
BooleanOperation(graphene_std::path_bool::BooleanOperation),
BooleanOperation(graphene_std::vector::misc::BooleanOperation),
}

View File

@ -28,6 +28,7 @@ const TEXT_REPLACEMENTS: &[(&str, &str)] = &[
),
("graphene_core::transform::Footprint", "graphene_core::transform::Footprint"),
("\"OptionalF64\":", "\"F64\":"),
("\"path_bool_nodes::BooleanOperation\"", "\"vector_types::vector::misc::BooleanOperation\""),
];
pub struct NodeReplacement<'a> {
@ -454,10 +455,10 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
],
},
// ================================
// path-bool
// path bool
// ================================
NodeReplacement {
node: graphene_std::path_bool::boolean_operation::IDENTIFIER,
node: graphene_std::path_bool_nodes::boolean_operation::IDENTIFIER,
aliases: &["graphene_path_bool::BooleanOperationNode", "graphene_std::vector::BooleanOperationNode"],
},
// ================================

View File

@ -23,11 +23,11 @@ use crate::messages::tool::common_functionality::transformation_cage::*;
use crate::messages::tool::common_functionality::utility_functions::{resize_bounds, rotate_bounds, skew_bounds, text_bounding_box, transforming_transform_cage};
use glam::DMat2;
use graph_craft::document::NodeId;
use graphene_std::path_bool::BooleanOperation;
use graphene_std::renderer::Quad;
use graphene_std::renderer::Rect;
use graphene_std::subpath::Subpath;
use graphene_std::transform::ReferencePoint;
use graphene_std::vector::misc::BooleanOperation;
use std::fmt;
#[derive(Default, ExtractField)]

View File

@ -1,2 +0,0 @@
/target/
test-results/

View File

@ -1,50 +0,0 @@
[package]
name = "path-bool"
version = "0.1.0"
rust-version = "1.85"
authors = ["Graphite Authors <contact@graphite.art>", "Adam Platkevič"]
edition = "2024"
keywords = [
"bezier",
"curve",
"boolean",
"path",
"geometry",
"computational geometry",
"vector graphics",
"2d",
"graphics",
]
categories = ["graphics", "mathematics"]
license = "MIT OR Apache-2.0"
[features]
logging = ["parsing"]
parsing = []
default = ["parsing"]
[dependencies]
glam = "0.29.0"
regex = "1.10.6"
slotmap = "1.0.7"
lyon_geom = "1.0"
roots = "0.0.8"
rustc-hash = "2.0.0"
smallvec = "1.13.2"
[dev-dependencies]
glob = "0.3"
svg = "0.18"
resvg = "0.47"
image = "0.25"
# Required dependencies
criterion = { workspace = true }
# Benchmarks
[[bench]]
name = "painted_dreams"
harness = false
[[bench]]
name = "path_segment_intersection"
harness = false

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,10 +0,0 @@
MIT License
Copyright (c) 2024 Adam Platkevič
Copyright (c) 2024 Graphite Authors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,15 +0,0 @@
NOTICE
Rust port and modifications are (c) 2024 Graphite Authors.
This library is derived from software originally developed by Adam Platkevič which is licensed under the MIT License reproduced below:
MIT License
Copyright (c) 2024 Adam Platkevič
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,69 +0,0 @@
# Path Bool
A Rust library for performing boolean operations on SVG paths.
Path Bool is a port of [PathBool.js](https://github.com/r-flash/PathBool.js), providing low-level functionality for boolean operations on complex 2D paths. It handles paths with multiple subpaths, self-intersections, and different fill rules.
## Features
- Supports multiple boolean operations: Union, Intersection, Difference, Exclusion, Division, and Fracture.
- Handles both `NonZero` and `EvenOdd` fill rules.
- Works with paths containing lines, cubic Bézier curves, quadratic Bézier curves, and elliptical arcs.
- Provides utilities for parsing and generating SVG path data.
## Installation
Add this to your `Cargo.toml`:
```toml
[dependencies]
path-bool = "0.1.0"
```
## Usage
Here's a basic example of performing an intersection operation on two paths:
```rust
use path_bool::{path_boolean, FillRule, PathBooleanOperation, path_from_path_data, path_to_path_data};
fn main() {
let path_a = path_from_path_data("M 10 10 L 50 10 L 30 40 Z").unwrap();
let path_b = path_from_path_data("M 20 30 L 60 30 L 60 50 L 20 50 Z").unwrap();
let result = path_boolean(
&path_a,
FillRule::NonZero,
&path_b,
FillRule::NonZero,
PathBooleanOperation::Intersection
).unwrap();
let result_data = path_to_path_data(&result[0], 0.001);
println!("Result: {}", result_data);
}
```
## Algorithm
The boolean operations are implemented using a graph-based approach. After the parsing the input, self-intersecting cubic beziers curves are simplified. Then the intersection points between all edges are calculated. These are then turned into a graph representation where every intersection becomes a new vertex. We then apply edge contractions to remove vertices with a degree of 2 to compute the [graph minor](https://en.wikipedia.org/wiki/Graph_minor). At this stage, identical edges are deduplicated. Because we are ultimately interested in the faces of the graph to decide if they should be included in the final output, we then compute the dual graph in which the faces become vertices and vertices become the new faces. That dual structure is then used to determine which faces (dual vertices) should be included in the final output.
## Development status
This project is a port of PathBool.js which is still in early stages of development. Contributions, bug reports, and feedback are welcome.
Future work includes:
- Comprehensive test suite
- Performance optimizations
- Additional examples and documentation
- Support for path builder tool features
## License and acknowledgements
This library is a Rust port of [PathBool.js](https://github.com/r-flash/PathBool.js) by Adam Platkevič.
It is dual-licensed under the MIT License or Apache-2.0 License. You may opt to comply with either license.
Copyright © 2024 Adam Platkevič
Copyright © 2024 Graphite Authors

View File

@ -1,18 +0,0 @@
use std::hint::black_box;
use criterion::{Criterion, criterion_group, criterion_main};
use path_bool::*;
pub fn criterion_benchmark(c: &mut Criterion) {
let path_a =
path_from_path_data("M0,340C161.737914,383.575765 107.564182,490.730587 273,476 C419,463 481.741198,514.692273 481.333333,768 C481.333333,768 -0,768 -0,768 C-0,768 0,340 0,340 Z").unwrap();
let path_b = path_from_path_data(
"M458.370270,572.165771C428.525848,486.720093 368.618805,467.485992 273,476 C107.564178,490.730591 161.737915,383.575775 0,340 C0,340 0,689 0,689 C56,700 106.513901,779.342590 188,694.666687 C306.607422,571.416260 372.033966,552.205139 458.370270,572.165771 Z",
).unwrap();
c.bench_function("painted_dreams_diff", |b| {
b.iter(|| path_boolean(black_box(&path_a), FillRule::NonZero, black_box(&path_b), FillRule::NonZero, PathBooleanOperation::Difference))
});
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

View File

@ -1,31 +0,0 @@
use std::hint::black_box;
use criterion::{Criterion, criterion_group, criterion_main};
use glam::DVec2;
use path_bool::*;
pub fn criterion_benchmark(crit: &mut Criterion) {
crit.bench_function("intersect 1", |bench| bench.iter(|| path_segment_intersection(black_box(&a()), black_box(&b()), true, &EPS)));
crit.bench_function("intersect 2", |bench| bench.iter(|| path_segment_intersection(black_box(&c()), black_box(&d()), true, &EPS)));
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
fn a() -> PathSegment {
PathSegment::Cubic(
DVec2::new(458.37027, 572.165771),
DVec2::new(428.525848, 486.720093),
DVec2::new(368.618805, 467.485992),
DVec2::new(273., 476.),
)
}
fn b() -> PathSegment {
PathSegment::Cubic(DVec2::new(273., 476.), DVec2::new(419., 463.), DVec2::new(481.741198, 514.692273), DVec2::new(481.333333, 768.))
}
fn c() -> PathSegment {
PathSegment::Cubic(DVec2::new(273., 476.), DVec2::new(107.564178, 490.730591), DVec2::new(161.737915, 383.575775), DVec2::new(0., 340.))
}
fn d() -> PathSegment {
PathSegment::Cubic(DVec2::new(0., 340.), DVec2::new(161.737914, 383.575765), DVec2::new(107.564182, 490.730587), DVec2::new(273., 476.))
}

View File

@ -1,333 +0,0 @@
#![expect(clippy::needless_doctest_main)]
#![doc = include_str!("../README.md")]
mod path_boolean;
// #[cfg(feature = "parsing")]
mod parsing {
pub(crate) mod path_command;
pub(crate) mod path_data;
}
mod util {
pub(crate) mod aabb;
pub(crate) mod epsilons;
pub(crate) mod grid;
pub(crate) mod math;
}
mod path;
#[cfg(test)]
mod visual_tests;
#[cfg(feature = "parsing")]
pub(crate) use parsing::*;
pub(crate) use path::*;
pub(crate) use util::*;
pub use intersection_path_segment::path_segment_intersection;
#[cfg(feature = "parsing")]
pub use parsing::path_data::{path_from_path_data, path_to_path_data};
pub use path_boolean::{BooleanError, EPS, FillRule, PathBooleanOperation, path_boolean};
pub use path_segment::PathSegment;
#[cfg(test)]
mod test {
use crate::path_boolean::{self, FillRule, PathBooleanOperation};
use crate::path_data::{path_from_path_data, path_to_path_data};
use path_boolean::path_boolean;
#[test]
fn square() {
let a = path_from_path_data("M 10 10 L 50 10 L 30 40 Z").unwrap();
let b = path_from_path_data("M 20 30 L 60 30 L 60 50 L 20 50 Z").unwrap();
let union = path_boolean(
&a,
path_boolean::FillRule::NonZero,
&b,
path_boolean::FillRule::NonZero,
path_boolean::PathBooleanOperation::Intersection,
)
.unwrap();
dbg!(path_to_path_data(&union[0], 0.001));
assert!(!union[0].is_empty());
}
#[test]
fn nesting_01() {
let a = path_from_path_data("M 47,24 A 23,23 0 0 1 24,47 23,23 0 0 1 1,24 23,23 0 0 1 24,1 23,23 0 0 1 47,24 Z").unwrap();
let b = path_from_path_data(
"M 37.909023,24 A 13.909023,13.909023 0 0 1 24,37.909023 13.909023,13.909023 0 0 1 10.090978,24 13.909023,13.909023 0 0 1 24,10.090978 13.909023,13.909023 0 0 1 37.909023,24 Z",
)
.unwrap();
let union = path_boolean(&a, path_boolean::FillRule::NonZero, &b, path_boolean::FillRule::NonZero, path_boolean::PathBooleanOperation::Union).unwrap();
dbg!(path_to_path_data(&union[0], 0.001));
assert!(!union[0].is_empty());
}
#[test]
fn semi_circle_join() {
let a = path_from_path_data(
"M 0.000000000000,0.000000000000 C 0.000000000000,0.000000000000 48.487723000000,-68.116546000000 51.950617000000,-96.987654000000 C 53.354187000000,-108.689605000000 5.171145000000,-241.394513000000 -0.000000000000,-254.901235000000 Z M 0.000000000000,0.000000000000 L -0.000000000000,-254.901235000000 Z"
).unwrap();
let b = path_from_path_data(
"M -0.000000000000,0.484328000000 C -0.000000000000,0.484328000000 -48.487723000000,-67.632218000000 -51.950617000000,-96.503326000000 C -53.354187000000,-108.205277000000 -5.171145000000,-240.910185000000 -0.000000000000,-254.416907000000 Z M -0.000000000000,0.484328000000 L -0.000000000000,-254.416907000000 Z",
)
.unwrap();
let result = path_boolean(&a, FillRule::NonZero, &b, FillRule::NonZero, PathBooleanOperation::Union).unwrap();
// Add assertions here based on expected results
assert_eq!(result.len(), 1, "Expected 1 resulting path for Union operation");
dbg!(path_to_path_data(&result[0], 0.001));
// Add more specific assertions about the resulting path if needed
assert!(!result[0].is_empty());
}
#[test]
fn nesting_02() {
let a = path_from_path_data("M 0.99999994,31.334457 C 122.61195,71.81859 -79.025816,-5.5803326 47,32.253367 V 46.999996 H 0.99999994 Z").unwrap();
let b = path_from_path_data("m 25.797222,29.08718 c 0,1.292706 -1.047946,2.340652 -2.340652,2.340652 -1.292707,0 -2.340652,-1.047946 -2.340652,-2.340652 0,-1.292707 1.047945,-2.340652 2.340652,-2.340652 1.292706,0 2.340652,1.047945 2.340652,2.340652 z M 7.5851073,28.332212 c 1e-7,1.292706 -1.0479456,2.340652 -2.3406521,2.340652 -1.2927063,-1e-6 -2.3406518,-1.047946 -2.3406517,-2.340652 -10e-8,-1.292707 1.0479454,-2.340652 2.3406517,-2.340652 1.2927065,-1e-6 2.3406522,1.047945 2.3406521,2.340652 z").unwrap();
let result = path_boolean(&a, FillRule::NonZero, &b, FillRule::NonZero, PathBooleanOperation::Union).unwrap();
// Add assertions here based on expected results
assert_eq!(result.len(), 1, "Expected 1 resulting path for Union operation");
// Add more specific assertions about the resulting path if needed
assert!(!result[0].is_empty());
}
#[test]
fn nesting_03() {
let a = path_from_path_data("m 21.829117,3.5444345 h 4.341766 V 16.502158 H 21.829117 Z M 47,24 A 23,23 0 0 1 24,47 23,23 0 0 1 1,24 23,23 0 0 1 24,1 23,23 0 0 1 47,24 Z").unwrap();
let b = path_from_path_data("M 24 6.4960938 A 17.504802 17.504802 0 0 0 6.4960938 24 A 17.504802 17.504802 0 0 0 24 41.503906 A 17.504802 17.504802 0 0 0 41.503906 24 A 17.504802 17.504802 0 0 0 24 6.4960938 z M 24 12.193359 A 11.805881 11.805881 0 0 1 35.806641 24 A 11.805881 11.805881 0 0 1 24 35.806641 A 11.805881 11.805881 0 0 1 12.193359 24 A 11.805881 11.805881 0 0 1 24 12.193359 z ").unwrap();
let result = path_boolean(&a, FillRule::NonZero, &b, FillRule::NonZero, PathBooleanOperation::Union).unwrap();
// Add assertions here based on expected results
assert_eq!(result.len(), 1, "Expected 1 resulting path for Union operation");
// Add more specific assertions about the resulting path if needed
let path_string = dbg!(path_to_path_data(&result[0], 0.001));
assert_eq!(path_string.chars().filter(|c| c == &'M').count(), 1, "More than one path returned");
assert!(!result[0].is_empty());
}
#[test]
fn simple_07() {
let a = path_from_path_data("M 37.671452,24 C 52.46888,31.142429 42.887716,37.358779 24,37.671452 16.4505,37.796429 10.328548,31.550534 10.328548,24 c 0,-7.550534 6.120918,-13.671452 13.671452,-13.671452 7.550534,0 6.871598,10.389295 13.671452,13.671452 z",
).unwrap();
let b = path_from_path_data("M 37.671452,24 C 33.698699,53.634887 29.50935,49.018306 24,37.671452 20.7021,30.879219 10.328548,31.550534 10.328548,24 c 0,-7.550534 6.120918,-13.671452 13.671452,-13.671452 7.550534,0 14.674677,6.187863 13.671452,13.671452 z").unwrap();
let result = path_boolean(&a, FillRule::NonZero, &b, FillRule::NonZero, PathBooleanOperation::Union).unwrap();
// Add assertions here based on expected results
assert_eq!(result.len(), 1, "Expected 1 resulting path for Union operation");
// Add more specific assertions about the resulting path if needed
dbg!(path_to_path_data(&result[0], 0.001));
assert!(!result[0].is_empty());
}
#[test]
fn rect_ellipse() {
let a = path_from_path_data("M0,0C0,0 100,0 100,0 C100,0 100,100 100,100 C100,100 0,100 0,100 C0,100 0,0 0,0 Z").unwrap();
let b = path_from_path_data("M50,0C77.589239,0 100,22.410761 100,50 C100,77.589239 77.589239,100 50,100 C22.410761,100 0,77.589239 0,50 C0,22.410761 22.410761,0 50,0 Z").unwrap();
let result = path_boolean(&a, FillRule::NonZero, &b, FillRule::NonZero, PathBooleanOperation::Union).unwrap();
// Add assertions here based on expected results
assert_eq!(result.len(), 1, "Expected 1 resulting path for Union operation");
dbg!(path_to_path_data(&result[0], 0.001));
assert!(!result[0].is_empty());
// Add more specific assertions about the resulting path if needed
}
#[test]
fn red_dress_loop() {
let a = path_from_path_data("M969.000000,0.000000C969.000000,0.000000 1110.066898,76.934393 1085.000000,181.000000 C1052.000000,318.000000 1199.180581,334.301571 1277.000000,319.000000 C1455.000000,284.000000 1586.999985,81.000000 1418.000000,0.000000 C1418.000000,0.000000 969.000000,0.000000 969.000000,0.000000").unwrap();
let b = path_from_path_data(
"M1211.000000,0.000000C1211.000000,0.000000 1255.000000,78.000000 1536.000000,95.000000 C1536.000000,95.000000 1536.000000,0.000000 1536.000000,0.000000 C1536.000000,0.000000 1211.000000,0.000000 1211.000000,0.000000 Z",
).unwrap();
let result = path_boolean(&a, FillRule::NonZero, &b, FillRule::NonZero, PathBooleanOperation::Intersection).unwrap();
// Add assertions here based on expected results
assert_eq!(result.len(), 1, "Expected 1 resulting path for Union operation");
dbg!(path_to_path_data(&result[0], 0.001));
// Add more specific assertions about the resulting path if needed
assert!(!result[0].is_empty());
}
#[test]
fn painted_dreams_1() {
let a = path_from_path_data("M969.000000,0.000000C969.000000,0.000000 1110.066898,76.934393 1085.000000,181.000000 C1052.000000,318.000000 1199.180581,334.301571 1277.000000,319.000000 C1455.000000,284.000000 1586.999985,81.000000 1418.000000,0.000000 C1418.000000,0.000000 969.000000,0.000000 969.000000,0.000000 Z").unwrap();
let b = path_from_path_data(
"M763.000000,0.000000C763.000000,0.000000 1536.000000,0.000000 1536.000000,0.000000 C1536.000000,0.000000 1536.000000,254.000000 1536.000000,254.000000 C1536.000000,254.000000 1462.000000,93.000000 1271.000000,199.000000 C1149.163056,266.616314 976.413656,188.510842 908.000000,134.000000 C839.586344,79.489158 763.000000,0.000000 763.000000,0.000000 Z",
).unwrap();
let result = path_boolean(&a, FillRule::NonZero, &b, FillRule::NonZero, PathBooleanOperation::Intersection).unwrap();
// Add assertions here based on expected results
assert_eq!(result.len(), 1, "Expected 1 resulting path for Union operation");
dbg!(path_to_path_data(&result[0], 0.001));
// Add more specific assertions about the resulting path if needed
assert!(!result[0].is_empty());
}
#[test]
fn painted_dreams_2() {
let a = path_from_path_data("M0,340C161.737914,383.575765 107.564182,490.730587 273,476 C419,463 481.741198,514.692273 481.333333,768 C481.333333,768 -0,768 -0,768 C-0,768 0,340 0,340 Z ")
.unwrap();
let b = path_from_path_data(
"M458.370270,572.165771C428.525848,486.720093 368.618805,467.485992 273,476 C107.564178,490.730591 161.737915,383.575775 0,340 C0,340 0,689 0,689 C56,700 106.513901,779.342590 188,694.666687 C306.607422,571.416260 372.033966,552.205139 458.370270,572.165771 Z",
).unwrap();
let result = path_boolean(&a, FillRule::NonZero, &b, FillRule::NonZero, PathBooleanOperation::Union).unwrap();
// Add assertions here based on expected results
assert_eq!(result.len(), 1, "Expected 1 resulting path for Union operation");
dbg!(path_to_path_data(&result[0], 0.001));
// Add more specific assertions about the resulting path if needed
assert!(!result[0].is_empty());
}
#[test]
fn painted_dreams_3() {
let a = path_from_path_data("M889,0C889,0 889,21 898,46 C909.595887,78.210796 872.365858,104.085306 869,147 C865,198 915,237 933,273 C951,309 951.703704,335.407407 923,349 C898.996281,360.366922 881,367 902,394 C923,421 928.592593,431.407407 898,468 C912.888889,472.888889 929.333333,513.333333 896,523 C896,523 876,533.333333 886,572 C896.458810,612.440732 873.333333,657.777778 802.666667,656.444444 C738.670245,655.236965 689,643 655,636 C621,629 604,623 585,666 C566,709 564,768 564,768 C564,768 0,768 0,768 C0,768 0,0 0,0 C0,0 889,0 889,0 Z ").unwrap();
let b = path_from_path_data(
"M552,768C552,768 993,768 993,768 C993,768 1068.918039,682.462471 1093,600 C1126,487 1007.352460,357.386071 957,324 C906.647540,290.613929 842,253 740,298 C638,343 491.342038,421.999263 491.342038,506.753005 C491.342038,641.999411 552,768 552,768 Z ",
).unwrap();
let result = path_boolean(&a, FillRule::NonZero, &b, FillRule::NonZero, PathBooleanOperation::Difference).unwrap();
// Add assertions here based on expected results
assert_eq!(result.len(), 1, "Expected 1 resulting path for Union operation");
dbg!(path_to_path_data(&result[0], 0.001));
// Add more specific assertions about the resulting path if needed
assert!(!result[0].is_empty());
}
#[test]
fn painted_dreams_4() {
let a = path_from_path_data("M458.370270,572.165771C372.033966,552.205139 306.607422,571.416260 188.000000,694.666687 C106.513901,779.342590 56.000000,700.000000 0.000000,689.000000 C0.000000,689.000000 0.000000,768.000000 0.000000,768.000000 C0.000000,768.000000 481.333344,768.000000 481.333344,768.000000 C481.474091,680.589417 474.095154,617.186768 458.370270,572.165771 Z ").unwrap();
let b = path_from_path_data(
"M364.000000,768.000000C272.000000,686.000000 294.333333,468.666667 173.333333,506.666667 C110.156241,526.507407 0.000000,608.000000 0.000000,608.000000 L -0.000000,768.000000 L 364.000000,768.000000 Z",
).unwrap();
let result = path_boolean(&a, FillRule::NonZero, &b, FillRule::NonZero, PathBooleanOperation::Difference).unwrap();
// Add assertions here based on expected results
assert_eq!(result.len(), 1, "Expected 1 resulting path for Union operation");
dbg!(path_to_path_data(&result[0], 0.001));
// Add more specific assertions about the resulting path if needed
assert!(!result[0].is_empty());
}
#[test]
fn painted_dreams_5() {
let a = path_from_path_data("M889.000000,0.000000C889.000000,0.000000 889.000000,21.000000 898.000000,46.000000 C909.595887,78.210796 872.365858,104.085306 869.000000,147.000000 C865.000000,198.000000 915.000000,237.000000 933.000000,273.000000 C951.000000,309.000000 951.703704,335.407407 923.000000,349.000000 C898.996281,360.366922 881.000000,367.000000 902.000000,394.000000 C923.000000,421.000000 928.592593,431.407407 898.000000,468.000000 C912.888889,472.888889 929.333333,513.333333 896.000000,523.000000 C896.000000,523.000000 876.000000,533.333333 886.000000,572.000000 C896.458810,612.440732 873.333333,657.777778 802.666667,656.444444 C738.670245,655.236965 689.000000,643.000000 655.000000,636.000000 C621.000000,629.000000 604.000000,623.000000 585.000000,666.000000 C566.000000,709.000000 564.000000,768.000000 564.000000,768.000000 C564.000000,768.000000 0.000000,768.000000 0.000000,768.000000 C0.000000,768.000000 0.000000,0.000000 0.000000,0.000000 C0.000000,0.000000 889.000000,0.000000 889.000000,0.000000 Z"
).unwrap();
let b = path_from_path_data(
"M891.555556,569.382716C891.555556,569.382716 883.555556,577.777778 879.111111,595.851852 C874.666667,613.925926 857.185185,631.407407 830.814815,633.777778 C804.444444,636.148148 765.629630,637.925926 708.148148,616.296296 C650.666667,594.666667 560.666667,568.000000 468.000000,487.333333 C375.333333,406.666667 283.333333,354.666667 283.333333,354.666667 C332.000000,330.666667 373.407788,298.323579 468.479950,219.785706 C495.739209,197.267187 505.084065,165.580817 514.452332,146.721008 C525.711584,124.054345 577.519713,94.951389 589.958848,64.658436 C601.152263,37.399177 601.175694,0.000010 601.175694,0.000000 C601.175694,0.000000 0.000000,0.000000 0.000000,0.000000 C0.000000,0.000000 0.000000,768.000000 0.000000,768.000000 C0.000000,768.000000 891.555556,768.000000 891.555556,768.000000 C891.555556,768.000000 891.555556,569.382716 891.555556,569.382716 Z",
).unwrap();
let result = path_boolean(&a, FillRule::NonZero, &b, FillRule::NonZero, PathBooleanOperation::Intersection).unwrap();
// Add assertions here based on expected results
assert_eq!(result.len(), 1, "Expected 1 resulting path for Union operation");
dbg!(path_to_path_data(&result[0], 0.001));
// Add more specific assertions about the resulting path if needed
assert!(!result[0].is_empty());
}
#[test]
fn painted_dreams_6() {
let a = path_from_path_data(
"M 969.000000000000,0.000000000000 C 969.000000000000,0.000000000000 1110.066900000000,76.934400000000 1085.000000000000,181.000000000000 C 1052.000000000000,318.000000000000 1199.180600000000,334.301600000000 1277.000000000000,319.000000000000 C 1455.000000000000,284.000000000000 1587.000000000000,81.000000000000 1418.000000000000,0.000000000000 C 1418.000000000000,0.000000000000 969.000000000000,0.000000000000 969.000000000000,0.000000000000 L 969.000000000000,0.000000000000"
).unwrap();
let b = path_from_path_data(
"M 763.000000000000,0.000000000000 C 763.000000000000,0.000000000000 1536.000000000000,0.000000000000 1536.000000000000,0.000000000000 C 1536.000000000000,0.000000000000 1536.000000000000,254.000000000000 1536.000000000000,254.000000000000 C 1536.000000000000,254.000000000000 1462.000000000000,93.000000000000 1271.000000000000,199.000000000000 C 1149.163100000000,266.616300000000 976.413700000000,188.510800000000 908.000000000000,134.000000000000 C 839.586300000000,79.489200000000 763.000000000000,0.000000000000 763.000000000000,0.000000000000 L 763.000000000000,0.000000000000",
).unwrap();
let result = path_boolean(&a, FillRule::NonZero, &b, FillRule::NonZero, PathBooleanOperation::Intersection).unwrap();
// Add assertions here based on expected results
assert_eq!(result.len(), 1, "Expected 1 resulting path for Union operation");
dbg!(path_to_path_data(&result[0], 0.001));
// Add more specific assertions about the resulting path if needed
assert!(!result[0].is_empty());
}
#[test]
fn painted_dreams_7() {
let a = path_from_path_data(
"M 989.666700000000,768.000000000000 C 989.666700000000,768.000000000000 1011.111100000000,786.399400000000 1011.111100000000,786.399400000000 C 1011.111100000000,786.399400000000 1299.306500000000,786.399400000000 1299.306500000000,786.399400000000 C 1299.306500000000,786.399400000000 1318.000000000000,768.000000000000 1318.000000000000,768.000000000000 C 1293.666700000000,681.000000000000 1173.363200000000,625.103600000000 1094.162400000000,594.296600000000 C 1094.162400000000,594.296600000000 1058.747200000000,687.805800000000 989.666700000000,768.000000000000"
).unwrap();
let b = path_from_path_data(
"M 983.155000000000,775.589300000000 L 1004.599400000000,793.988700000000 L 1007.409000000000,796.399400000000 L 1011.111100000000,796.399400000000 L 1299.306500000000,796.399400000000 L 1303.402200000000,796.399400000000 L 1306.321200000000,793.526300000000 L 1325.014800000000,775.126900000000 L 1329.236900000000,770.971200000000 L 1327.630400000000,765.306400000000 C 1302.280700000000,675.920800000000 1179.503900000000,617.211200000000 1097.787500000000,584.976800000000 L 1088.418100000000,581.280900000000 L 1084.806400000000,590.765700000000 C 1084.117400000000,592.575300000000 1049.449700000000,683.516200000000 982.090100000000,761.473400000000 L 975.539200000000,769.055000000000 L 983.155000000000,775.589300000000 M 1003.696800000000,766.861600000000 C 1068.901100000000,687.878900000000 1102.806400000000,599.696700000000 1103.497000000000,597.883400000000 L 1090.537200000000,603.616300000000 C 1165.521500000000,632.344400000000 1279.846400000000,683.736400000000 1306.585700000000,765.203400000000 L 1295.210700000000,776.399400000000 L 1014.813100000000,776.399400000000 L 1003.696800000000,766.861600000000",
).unwrap();
let result = path_boolean(&a, FillRule::NonZero, &b, FillRule::NonZero, PathBooleanOperation::Difference).unwrap();
// Add assertions here based on expected results
assert_eq!(result.len(), 1, "Expected 1 resulting path for Union operation");
dbg!(path_to_path_data(&result[0], 0.001));
// Add more specific assertions about the resulting path if needed
assert!(!result[0].is_empty());
}
#[test]
fn blobs() {
let a = path_from_path_data(
"m658.03348 118.4966c7.85928 4.83645 114.84582 7.8304 127.89652 6.52531 20.97932-2.09799 43.06722-24.79623 43.06722-24.79623 0 0-96.43723-26.02101-108.97311-28.54836-20.22849-4.07832-78.95651 36.37872-61.99063 46.81928z
m658.03348 115.88649c40.45718-30.01653 82.213-45.24662 103.10032-31.32163 7.83037 5.2203-3.58567 22.51547 13.05064 39.152 3.91519 3.9152-129.49099 2.06705-116.15096-7.83037z
m680.87214 56.0165c2.20775-9.60391 62.6449-29.65403 101.79518-30.01652 17.61846-0.16312 119.39605 40.30737 130.50668 54.8128 5.8045 7.57806-76.88558 29.08762-91.35464 31.32162-15.28899 2.36056-144.20983-41.92525-140.94722-56.1179z"
).unwrap();
let b = path_from_path_data("").unwrap();
let result = path_boolean(&a, FillRule::NonZero, &b, FillRule::NonZero, PathBooleanOperation::Union).unwrap();
// Add assertions here based on expected results
assert_eq!(result.len(), 1, "Expected 1 resulting path for Union operation");
dbg!(path_to_path_data(&result[0], 0.001));
// Add more specific assertions about the resulting path if needed
assert!(!result[0].is_empty());
}
#[test]
fn shared_line() {
let a = path_from_path_data(
"m 658.03348,118.4966 c 7.85928,4.83645 114.84582,7.8304 127.89652,6.52531 20.97932,-2.09799 43.06722,-24.79623 43.06722,-24.79623 0,0 -96.43723,-26.02101 -108.97311,-28.54836 -20.22849,-4.07832 -78.95651,36.37872 -61.99063,46.81928 Z"
).unwrap();
let b = path_from_path_data(
"m 658.03348,115.88649 c 40.45718,-30.01653 82.213,-45.24662 103.10032,-31.32163 7.83037,5.2203 -3.58567,22.51547 13.05064,39.152 3.91519,3.9152 -129.49099,2.06705 -116.15096,-7.83037 z",
)
.unwrap();
let result = path_boolean(&a, FillRule::NonZero, &b, FillRule::NonZero, PathBooleanOperation::Union).unwrap();
// Add assertions here based on expected results
assert_eq!(result.len(), 1, "Expected 1 resulting path for Union operation");
dbg!(path_to_path_data(&result[0], 0.001));
// Add more specific assertions about the resulting path if needed
assert!(!result[0].is_empty());
}
#[test]
fn leaf() {
let a = path_from_path_data(
"M 0.000000000000,0.000000000000 C 0.000000000000,0.000000000000 30.204085760000,16.549763300000 72.719386340000,26.932261410000 C 72.719386340000,26.932261410000 70.254313510000,17.656445030000 64.511458520000,11.328808640000 C 64.511458520000,11.328808640000 87.851933730000,7.175523990000 112.835205650000,14.952290640000 C 112.835205650000,14.952290640000 85.410272600000,-0.944114870000 126.097112900000,-8.575894450000 C 118.337466460000,-27.271823700000 151.641256810000,-26.435638610000 157.566270790000,-37.101186050000 C 157.566270790000,-37.101186050000 140.392289790000,-20.685582680000 114.877703390000,-41.337145860000 C 99.535241580000,-45.054042580000 93.348771040000,-46.907137740000 91.251365300000,-51.920903790000 C 88.782358670000,-57.822967180000 120.444444440000,-82.851851850000 141.777777780000,-86.604938270000 C 141.777777780000,-86.604938270000 123.802469140000,-82.654320990000 114.716049380000,-90.160493830000 C 105.629629630000,-97.666666670000 112.345679010000,-115.000000000000 125.382716050000,-124.679012350000 C 125.382716050000,-124.679012350000 112.740740740000,-117.962962960000 115.506172840000,-132.382716050000 C 118.271604940000,-146.802469140000 109.740281350000,-157.864197530000 136.839506170000,-177.617283950000 C 115.506172840000,-165.567901230000 97.333333330000,-150.358024690000 92.395061730000,-161.419753090000 C 81.925925930000,-147.987654320000 66.518518520000,-140.481481480000 66.320987650000,-154.308641980000 C 59.703703700000,-139.000000000000 54.074074070000,-137.518518520000 49.135802470000,-136.333333330000 C 47.555555560000,-141.864197530000 43.456790120000,-172.333333330000 63.802469140000,-190.901234570000 C 48.888888890000,-178.259259260000 20.444444440000,-172.037037040000 28.444444440000,-210.456790120000 C 23.506172840000,-203.938271600000 13.234567900000,-196.432098770000 0.000000000000,-231.000000000000 L 0.000000000000,0.000000000000 Z"
).unwrap();
let b = path_from_path_data(
"M 0.100000000000,0.000000000000 C 0.100000000000,0.000000000000 -30.104085760000,16.549763300000 -72.619386340000,26.932261410000 C -72.619386340000,26.932261410000 -70.154313510000,17.656445030000 -64.411458520000,11.328808640000 C -64.411458520000,11.328808640000 -87.751933730000,7.175523990000 -112.735205650000,14.952290640000 C -112.735205650000,14.952290640000 -85.310272600000,-0.944114870000 -125.997112900000,-8.575894450000 C -118.237466460000,-27.271823700000 -151.541256810000,-26.435638610000 -157.466270790000,-37.101186050000 C -157.466270790000,-37.101186050000 -140.292289790000,-20.685582680000 -114.777703390000,-41.337145860000 C -99.435241580000,-45.054042580000 -93.248771040000,-46.907137740000 -91.151365300000,-51.920903790000 C -88.682358670000,-57.822967180000 -120.344444440000,-82.851851850000 -141.677777780000,-86.604938270000 C -141.677777780000,-86.604938270000 -123.702469140000,-82.654320990000 -114.616049380000,-90.160493830000 C -105.529629630000,-97.666666670000 -112.245679010000,-115.000000000000 -125.282716050000,-124.679012350000 C -125.282716050000,-124.679012350000 -112.640740740000,-117.962962960000 -115.406172840000,-132.382716050000 C -118.171604940000,-146.802469140000 -109.640281350000,-157.864197530000 -136.739506170000,-177.617283950000 C -115.406172840000,-165.567901230000 -97.233333330000,-150.358024690000 -92.295061730000,-161.419753090000 C -81.825925930000,-147.987654320000 -66.418518520000,-140.481481480000 -66.220987650000,-154.308641980000 C -59.603703700000,-139.000000000000 -53.974074070000,-137.518518520000 -49.035802470000,-136.333333330000 C -47.455555560000,-141.864197530000 -43.356790120000,-172.333333330000 -63.702469140000,-190.901234570000 C -48.788888890000,-178.259259260000 -20.344444440000,-172.037037040000 -28.344444440000,-210.456790120000 C -23.406172840000,-203.938271600000 -13.134567900000,-196.432098770000 0.100000000000,-231.000000000000 L 0.100000000000,0.000000000000 Z",
)
.unwrap();
let result = path_boolean(&a, FillRule::NonZero, &b, FillRule::NonZero, PathBooleanOperation::Union).unwrap();
// Add assertions here based on expected results
assert_eq!(result.len(), 1, "Expected 1 resulting path for Union operation");
dbg!(path_to_path_data(&result[0], 0.001));
// Add more specific assertions about the resulting path if needed
assert_eq!(result.len(), 1);
assert!(!result[0].is_empty());
}
#[test]
fn real_01() {
let a = path_from_path_data(
"M 212.67152,105 A 64.171516,64.171516 0 0 1 148.5,169.17152 64.171516,64.171516 0 0 1 84.328484,105 64.171516,64.171516 0 0 1 148.5,40.828484 64.171516,64.171516 0 0 1 212.67152,105 Z",
)
.unwrap();
let b = path_from_path_data(
"m 83.755387,112.6962 h -7.62 v 37.0332 h 7.62 z m 22.097973,9.144 c -3.4544,0 -5.892801,1.4732 -7.620001,4.5212 v -4.064 H 91.12136 v 38.5064 h 7.111999 v -14.3256 c 1.7272,3.048 4.165601,4.4704 7.620001,4.4704 6.604,0 11.4808,-6.1976 11.4808,-14.5288 0,-8.0772 -4.3688,-14.5796 -11.4808,-14.5796 z m -1.6256,5.9436 c 3.6068,0 5.9944,3.5052 5.9944,8.7376 0,4.9784 -2.4892,8.4836 -5.9944,8.4836 -3.556,0 -5.994401,-3.4544 -5.994401,-8.5852 0,-5.1308 2.438401,-8.636 5.994401,-8.636 z m 23.62201,13.97 h -6.9596 c 0.2032,5.9944 4.6228,9.144 12.954,9.144 9.6012,0 11.9888,-5.4864 11.9888,-9.2964 0,-3.556 -1.778,-5.842 -5.3848,-6.9088 l -8.9916,-2.5908 c -1.9812,-0.6096 -2.4892,-1.016 -2.4892,-2.1336 0,-1.524 1.6256,-2.54 4.1148,-2.54 3.4036,0 5.08,1.2192 5.1308,3.7084 h 6.858 c -0.1016,-5.7912 -4.572,-9.2964 -11.938,-9.2964 -6.9596,0 -11.2776,3.5052 -11.2776,9.144 0,5.3848 4.4196,6.2484 5.9944,6.7564 l 8.4836,2.6416 c 1.778,0.5588 2.3876,1.1176 2.3876,2.2352 0,1.6764 -1.9812,2.6924 -5.2832,2.6924 -4.4704,0 -5.1816,-1.6764 -5.588,-3.556 z m 47.59959,7.9756 v -27.432 h -7.112 v 17.1704 c 0,3.2512 -2.286,5.3848 -5.7404,5.3848 -3.048,0 -4.572,-1.6256 -4.572,-4.9276 v -17.6276 h -7.112 v 19.1008 c 0,6.0452 3.3528,9.4996 9.1948,9.4996 3.7084,0 6.1976,-1.3716 8.2296,-4.4196 v 3.2512 z m 6.60404,-27.432 v 27.432 h 7.112 v -16.4592 c 0,-3.3528 1.8288,-5.3848 4.8768,-5.3848 2.3876,0 3.8608,1.3716 3.8608,3.556 v 18.288 h 7.112 v -16.4592 c 0,-3.3528 1.8288,-5.3848 4.8768,-5.3848 2.3876,0 3.8608,1.3716 3.8608,3.556 v 18.288 h 7.112 v -19.4056 c 0,-5.334 -3.2512,-8.4836 -8.7376,-8.4836 -3.4544,0 -5.8928,1.2192 -8.0264,4.064 -1.3208,-2.5908 -4.064,-4.064 -7.4676,-4.064 -3.1496,0 -5.1816,1.0668 -7.5184,3.8608 v -3.4036 z M 81.012192,49.196201 h -7.62 v 37.0332 h 25.3492 v -6.35 h -17.7292 z m 35.305978,9.144 c -8.382,0 -13.5128,5.5372 -13.5128,14.5288 0,9.0424 5.1308,14.5288 13.5636,14.5288 8.3312,0 13.5636,-5.5372 13.5636,-14.3256 0,-9.2964 -5.0292,-14.732 -13.6144,-14.732 z m 0.0508,5.7404 c 3.9116,0 6.4516,3.5052 6.4516,8.89 0,5.1308 -2.6416,8.6868 -6.4516,8.6868 -3.8608,0 -6.4516,-3.556 -6.4516,-8.7884 0,-5.2324 2.5908,-8.7884 6.4516,-8.7884 z m 18.89761,-5.2832 v 27.432 h 7.112 v -14.5796 c 0,-4.1656 2.0828,-6.2484 6.2484,-6.2484 0.762,0 1.27,0.0508 2.2352,0.2032 v -7.2136 c -0.4064,-0.0508 -0.6604,-0.0508 -0.8636,-0.0508 -3.2512,0 -6.096,2.1336 -7.62,5.842 v -5.3848 z m 31.34362,-0.4572 c -7.874,0 -12.7,5.6896 -12.7,14.8844 0,8.7884 4.7752,14.1732 12.5476,14.1732 6.14679,0 11.12519,-3.5052 12.69999,-8.89 h -7.0104 c -0.8636,2.1844 -2.8448,3.4544 -5.43559,3.4544 -4.7752,0 -5.5372,-3.5052 -5.6896,-7.2136 h 18.38959 c 0.0508,-0.6096 0.0508,-0.8636 0.0508,-1.2192 0,-12.1412 -7.366,-15.1892 -12.85239,-15.1892 z m 5.43559,11.684 H 161.1238 c 0.4572,-4.1656 2.2352,-6.2484 5.3848,-6.2484 3.30199,0 5.23239,2.2352 5.53719,6.2484 z m 12.75081,-11.2268 v 27.432 h 7.112 v -16.4592 c 0,-3.3528 1.8288,-5.3848 4.8768,-5.3848 2.3876,0 3.8608,1.3716 3.8608,3.556 v 18.288 h 7.112 v -16.4592 c 0,-3.3528 1.8288,-5.3848 4.8768,-5.3848 2.3876,0 3.8608,1.3716 3.8608,3.556 v 18.288 h 7.112 v -19.4056 c 0,-5.334 -3.2512,-8.4836 -8.7376,-8.4836 -3.4544,0 -5.8928,1.2192 -8.0264,4.064 -1.3208,-2.5908 -4.064,-4.064 -7.4676,-4.064 -3.1496,0 -5.1816,1.0668 -7.5184,3.8608 v -3.4036 z",
)
.unwrap();
let result = path_boolean(&a, FillRule::NonZero, &b, FillRule::NonZero, PathBooleanOperation::Union).unwrap();
// Add assertions here based on expected results
assert_eq!(result.len(), 1, "Expected 1 resulting path for Union operation");
dbg!(path_to_path_data(&result[0], 0.001));
// Add more specific assertions about the resulting path if needed
assert!(!result[0].is_empty());
}
}

View File

@ -1,124 +0,0 @@
use glam::DVec2;
#[derive(Clone, Debug)]
pub enum AbsolutePathCommand {
H(f64),
V(f64),
M(DVec2),
L(DVec2),
C(DVec2, DVec2, DVec2),
S(DVec2, DVec2),
Q(DVec2, DVec2),
T(DVec2),
A(f64, f64, f64, bool, bool, DVec2),
Z,
}
#[derive(Clone, Debug)]
pub enum RelativePathCommand {
H(f64),
V(f64),
M(f64, f64),
L(f64, f64),
C(f64, f64, f64, f64, f64, f64),
S(f64, f64, f64, f64),
Q(f64, f64, f64, f64),
T(f64, f64),
A(f64, f64, f64, bool, bool, f64, f64),
}
#[derive(Clone, Debug)]
pub enum PathCommand {
Absolute(AbsolutePathCommand),
Relative(RelativePathCommand),
}
pub fn to_absolute_commands<I>(commands: I) -> impl Iterator<Item = AbsolutePathCommand>
where
I: IntoIterator<Item = PathCommand>,
{
let mut last_point = DVec2::ZERO;
let mut first_point = last_point;
commands.into_iter().flat_map(move |cmd| match cmd {
PathCommand::Absolute(abs_cmd) => {
match abs_cmd {
AbsolutePathCommand::H(x) => {
last_point.x = x;
}
AbsolutePathCommand::V(y) => {
last_point.y = y;
}
AbsolutePathCommand::M(point) => {
last_point = point;
first_point = point;
}
AbsolutePathCommand::L(point) => {
last_point = point;
}
AbsolutePathCommand::C(_, _, end) => {
last_point = end;
}
AbsolutePathCommand::S(_, end) => {
last_point = end;
}
AbsolutePathCommand::Q(_, end) => {
last_point = end;
}
AbsolutePathCommand::T(end) => {
last_point = end;
}
AbsolutePathCommand::A(_, _, _, _, _, end) => {
last_point = end;
}
AbsolutePathCommand::Z => {
last_point = first_point;
}
}
vec![abs_cmd]
}
PathCommand::Relative(rel_cmd) => match rel_cmd {
RelativePathCommand::H(dx) => {
last_point.x += dx;
vec![AbsolutePathCommand::L(last_point)]
}
RelativePathCommand::V(dy) => {
last_point.y += dy;
vec![AbsolutePathCommand::L(last_point)]
}
RelativePathCommand::M(dx, dy) => {
last_point += DVec2::new(dx, dy);
first_point = last_point;
vec![AbsolutePathCommand::M(last_point)]
}
RelativePathCommand::L(dx, dy) => {
last_point += DVec2::new(dx, dy);
vec![AbsolutePathCommand::L(last_point)]
}
RelativePathCommand::C(dx1, dy1, dx2, dy2, dx, dy) => {
let c1 = last_point + DVec2::new(dx1, dy1);
let c2 = last_point + DVec2::new(dx2, dy2);
last_point += DVec2::new(dx, dy);
vec![AbsolutePathCommand::C(c1, c2, last_point)]
}
RelativePathCommand::S(dx2, dy2, dx, dy) => {
let c2 = last_point + DVec2::new(dx2, dy2);
last_point += DVec2::new(dx, dy);
vec![AbsolutePathCommand::S(c2, last_point)]
}
RelativePathCommand::Q(dx1, dy1, dx, dy) => {
let control = last_point + DVec2::new(dx1, dy1);
last_point += DVec2::new(dx, dy);
vec![AbsolutePathCommand::Q(control, last_point)]
}
RelativePathCommand::T(dx, dy) => {
last_point += DVec2::new(dx, dy);
vec![AbsolutePathCommand::T(last_point)]
}
RelativePathCommand::A(rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, dx, dy) => {
last_point += DVec2::new(dx, dy);
vec![AbsolutePathCommand::A(rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, last_point)]
}
},
})
}

View File

@ -1,168 +0,0 @@
use crate::BooleanError;
use crate::path::{Path, path_from_commands, path_to_commands};
use crate::path_command::{AbsolutePathCommand, PathCommand, RelativePathCommand};
use glam::DVec2;
use regex::Regex;
pub fn commands_from_path_data(d: &str) -> Result<Vec<PathCommand>, BooleanError> {
let re_float = Regex::new(r"^\s*,?\s*(-?\d*(?:\d\.|\.\d|\d)\d*(?:[eE][+\-]?\d+)?)").unwrap();
let re_cmd = Regex::new(r"^\s*([MLCSQTAZHVmlhvcsqtaz])").unwrap();
let re_bool = Regex::new(r"^\s*,?\s*([01])").unwrap();
let mut i = 0;
let mut last_cmd = 'M';
let mut commands = Vec::new();
let get_cmd = |i: &mut usize, last_cmd: char| -> Option<char> {
if *i >= d.len() - 1.min(d.len()) {
return None;
}
if let Some(cap) = re_cmd.captures(&d[*i..]) {
*i += cap[0].len();
Some(cap[1].chars().next().unwrap())
} else {
match last_cmd {
'M' => Some('L'),
'm' => Some('l'),
'z' | 'Z' => None,
_ => Some(last_cmd),
}
}
};
let get_float = |i: &mut usize| -> f64 {
if let Some(cap) = re_float.captures(&d[*i..]) {
*i += cap[0].len();
cap[1].parse().unwrap()
} else {
panic!("Invalid path data. Expected a number at index {}, got {}", i, &d[*i..]);
}
};
let get_bool = |i: &mut usize| -> bool {
if let Some(cap) = re_bool.captures(&d[*i..]) {
*i += cap[0].len();
&cap[1] == "1"
} else {
panic!("Invalid path data. Expected a flag at index {i}");
}
};
while let Some(cmd) = get_cmd(&mut i, last_cmd) {
last_cmd = cmd;
match cmd {
'M' => commands.push(PathCommand::Absolute(AbsolutePathCommand::M(DVec2::new(get_float(&mut i), get_float(&mut i))))),
'L' => commands.push(PathCommand::Absolute(AbsolutePathCommand::L(DVec2::new(get_float(&mut i), get_float(&mut i))))),
'C' => commands.push(PathCommand::Absolute(AbsolutePathCommand::C(
DVec2::new(get_float(&mut i), get_float(&mut i)),
DVec2::new(get_float(&mut i), get_float(&mut i)),
DVec2::new(get_float(&mut i), get_float(&mut i)),
))),
'S' => commands.push(PathCommand::Absolute(AbsolutePathCommand::S(
DVec2::new(get_float(&mut i), get_float(&mut i)),
DVec2::new(get_float(&mut i), get_float(&mut i)),
))),
'Q' => commands.push(PathCommand::Absolute(AbsolutePathCommand::Q(
DVec2::new(get_float(&mut i), get_float(&mut i)),
DVec2::new(get_float(&mut i), get_float(&mut i)),
))),
'T' => commands.push(PathCommand::Absolute(AbsolutePathCommand::T(DVec2::new(get_float(&mut i), get_float(&mut i))))),
'A' => commands.push(PathCommand::Absolute(AbsolutePathCommand::A(
get_float(&mut i),
get_float(&mut i),
get_float(&mut i),
get_bool(&mut i),
get_bool(&mut i),
DVec2::new(get_float(&mut i), get_float(&mut i)),
))),
'Z' | 'z' => commands.push(PathCommand::Absolute(AbsolutePathCommand::Z)),
'H' => commands.push(PathCommand::Absolute(AbsolutePathCommand::H(get_float(&mut i)))),
'V' => commands.push(PathCommand::Absolute(AbsolutePathCommand::V(get_float(&mut i)))),
'm' => commands.push(PathCommand::Relative(RelativePathCommand::M(get_float(&mut i), get_float(&mut i)))),
'l' => commands.push(PathCommand::Relative(RelativePathCommand::L(get_float(&mut i), get_float(&mut i)))),
'h' => commands.push(PathCommand::Relative(RelativePathCommand::H(get_float(&mut i)))),
'v' => commands.push(PathCommand::Relative(RelativePathCommand::V(get_float(&mut i)))),
'c' => commands.push(PathCommand::Relative(RelativePathCommand::C(
get_float(&mut i),
get_float(&mut i),
get_float(&mut i),
get_float(&mut i),
get_float(&mut i),
get_float(&mut i),
))),
's' => commands.push(PathCommand::Relative(RelativePathCommand::S(
get_float(&mut i),
get_float(&mut i),
get_float(&mut i),
get_float(&mut i),
))),
'q' => commands.push(PathCommand::Relative(RelativePathCommand::Q(
get_float(&mut i),
get_float(&mut i),
get_float(&mut i),
get_float(&mut i),
))),
't' => commands.push(PathCommand::Relative(RelativePathCommand::T(get_float(&mut i), get_float(&mut i)))),
'a' => commands.push(PathCommand::Relative(RelativePathCommand::A(
get_float(&mut i),
get_float(&mut i),
get_float(&mut i),
get_bool(&mut i),
get_bool(&mut i),
get_float(&mut i),
get_float(&mut i),
))),
_ => return Err(BooleanError::InvalidPathCommand(cmd)),
}
}
Ok(commands)
}
pub fn path_from_path_data(d: &str) -> Result<Path, BooleanError> {
Ok(path_from_commands(commands_from_path_data(d)?).collect())
}
pub fn path_to_path_data(path: &Path, eps: f64) -> String {
path_to_commands(path.iter(), eps)
.map(|cmd| match cmd {
PathCommand::Absolute(abs_cmd) => match abs_cmd {
AbsolutePathCommand::H(dx) => format!("H {dx:.12}"),
AbsolutePathCommand::V(dy) => format!("V {dy:.12}"),
AbsolutePathCommand::M(p) => format!("M {:.12},{:.12}", p.x, p.y),
AbsolutePathCommand::L(p) => format!("L {:.12},{:.12}", p.x, p.y),
AbsolutePathCommand::C(p1, p2, p3) => format!("C {:.12},{:.12} {:.12},{:.12} {:.12},{:.12}", p1.x, p1.y, p2.x, p2.y, p3.x, p3.y),
AbsolutePathCommand::S(p1, p2) => {
format!("S {:.12},{:.12} {:.12},{:.12}", p1.x, p1.y, p2.x, p2.y)
}
AbsolutePathCommand::Q(p1, p2) => {
format!("Q {:.12},{:.12} {:.12},{:.12}", p1.x, p1.y, p2.x, p2.y)
}
AbsolutePathCommand::T(p) => format!("T {:.12},{:.12}", p.x, p.y),
AbsolutePathCommand::A(rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, p) => {
format!("A {:.12} {:.12} {:.12} {} {} {:.12},{:.12}", rx, ry, x_axis_rotation, large_arc_flag as u8, sweep_flag as u8, p.x, p.y)
}
AbsolutePathCommand::Z => "Z".to_string(),
},
PathCommand::Relative(rel_cmd) => match rel_cmd {
RelativePathCommand::M(dx, dy) => format!("m {dx:.12},{dy:.12}"),
RelativePathCommand::L(dx, dy) => format!("l {dx:.12},{dy:.12}"),
RelativePathCommand::H(dx) => format!("h {dx:.12}"),
RelativePathCommand::V(dy) => format!("v {dy:.12}"),
RelativePathCommand::C(dx1, dy1, dx2, dy2, dx, dy) => format!("c{dx1:.12},{dy1:.12} {dx2:.12},{dy2:.12} {dx:.12},{dy:.12}"),
RelativePathCommand::S(dx2, dy2, dx, dy) => {
format!("s {dx2:.12},{dy2:.12} {dx:.12},{dy:.12}")
}
RelativePathCommand::Q(dx1, dy1, dx, dy) => {
format!("q {dx1:.12},{dy1:.12} {dx:.12},{dy:.12}")
}
RelativePathCommand::T(dx, dy) => format!("t{dx:.12},{dy:.12}"),
RelativePathCommand::A(rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, dx, dy) => {
format!("a {:.12} {:.12} {:.12} {} {} {:.12},{:.12}", rx, ry, x_axis_rotation, large_arc_flag as u8, sweep_flag as u8, dx, dy)
}
},
})
.collect::<Vec<String>>()
.join(" ")
}

View File

@ -1,140 +0,0 @@
pub(crate) mod intersection_path_segment;
pub(crate) mod line_segment;
pub(crate) mod line_segment_aabb;
pub(crate) mod path_cubic_segment_self_intersection;
pub(crate) mod path_segment;
use glam::DVec2;
#[cfg(feature = "parsing")]
use crate::path_command::{AbsolutePathCommand, PathCommand, to_absolute_commands};
use crate::path_segment::PathSegment;
pub type Path = Vec<PathSegment>;
fn reflect_control_point(point: DVec2, control_point: DVec2) -> DVec2 {
point * 2. - control_point
}
#[cfg(feature = "parsing")]
pub fn path_from_commands<I>(commands: I) -> impl Iterator<Item = PathSegment>
where
I: IntoIterator<Item = PathCommand>,
{
let mut first_point: Option<DVec2> = None;
let mut last_point: Option<DVec2> = None;
let mut last_control_point: Option<DVec2> = None;
to_absolute_commands(commands).filter_map(move |cmd| match cmd {
AbsolutePathCommand::M(point) => {
last_point = Some(point);
first_point = Some(point);
last_control_point = None;
None
}
AbsolutePathCommand::L(point) => {
let start = last_point.unwrap();
last_point = Some(point);
last_control_point = None;
Some(PathSegment::Line(start, point))
}
AbsolutePathCommand::H(x) => {
let start = last_point.unwrap();
let point = DVec2::new(x, start.y);
last_point = Some(point);
last_control_point = None;
Some(PathSegment::Line(start, point))
}
AbsolutePathCommand::V(y) => {
let start = last_point.unwrap();
let point = DVec2::new(start.x, y);
last_point = Some(point);
last_control_point = None;
Some(PathSegment::Line(start, point))
}
AbsolutePathCommand::C(c1, c2, end) => {
let start = last_point.unwrap();
last_point = Some(end);
last_control_point = Some(c2);
Some(PathSegment::Cubic(start, c1, c2, end))
}
AbsolutePathCommand::S(c2, end) => {
let start = last_point.unwrap();
let c1 = reflect_control_point(start, last_control_point.unwrap_or(start));
last_point = Some(end);
last_control_point = Some(c2);
Some(PathSegment::Cubic(start, c1, c2, end))
}
AbsolutePathCommand::Q(c, end) => {
let start = last_point.unwrap();
last_point = Some(end);
last_control_point = Some(c);
Some(PathSegment::Quadratic(start, c, end))
}
AbsolutePathCommand::T(end) => {
let start = last_point.unwrap();
let c = reflect_control_point(start, last_control_point.unwrap_or(start));
last_point = Some(end);
last_control_point = Some(c);
Some(PathSegment::Quadratic(start, c, end))
}
AbsolutePathCommand::A(rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, end) => {
let start = last_point.unwrap();
last_point = Some(end);
last_control_point = None;
Some(PathSegment::Arc(start, rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, end))
}
AbsolutePathCommand::Z => {
let start = last_point.unwrap();
let end = first_point.unwrap();
last_point = Some(end);
last_control_point = None;
Some(PathSegment::Line(start, end))
}
})
}
#[cfg(feature = "parsing")]
pub fn path_to_commands<'a, I>(segments: I, eps: f64) -> impl Iterator<Item = PathCommand> + 'a
where
I: IntoIterator<Item = &'a PathSegment> + 'a,
{
let mut last_point: Option<DVec2> = None;
segments
.into_iter()
.flat_map(move |seg| {
let start = seg.start();
let mut commands = Vec::new();
if last_point.is_none_or(|lp| !start.abs_diff_eq(lp, eps)) {
if last_point.is_some() {
commands.push(PathCommand::Absolute(AbsolutePathCommand::Z));
}
commands.push(PathCommand::Absolute(AbsolutePathCommand::M(start)));
}
match seg {
PathSegment::Line(_, end) => {
commands.push(PathCommand::Absolute(AbsolutePathCommand::L(*end)));
last_point = Some(*end);
}
PathSegment::Cubic(_, c1, c2, end) => {
commands.push(PathCommand::Absolute(AbsolutePathCommand::C(*c1, *c2, *end)));
last_point = Some(*end);
}
PathSegment::Quadratic(_, c, end) => {
commands.push(PathCommand::Absolute(AbsolutePathCommand::Q(*c, *end)));
last_point = Some(*end);
}
PathSegment::Arc(_, rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, end) => {
commands.push(PathCommand::Absolute(AbsolutePathCommand::A(*rx, *ry, *x_axis_rotation, *large_arc_flag, *sweep_flag, *end)));
last_point = Some(*end);
}
}
commands
})
.chain(std::iter::once(PathCommand::Absolute(AbsolutePathCommand::Z)))
}

View File

@ -1,313 +0,0 @@
use crate::aabb::{Aabb, bounding_box_max_extent, bounding_boxes_overlap};
use crate::epsilons::Epsilons;
use crate::line_segment::{line_segment_intersection, line_segments_intersect};
use crate::line_segment_aabb::line_segment_aabb_intersect;
use crate::math::lerp;
use crate::path_segment::PathSegment;
use glam::DVec2;
use lyon_geom::{CubicBezierSegment, Point};
/// Convert PathSegment::Cubic to lyon_geom::CubicBezierSegment
fn path_segment_cubic_to_lyon(start: DVec2, ctrl1: DVec2, ctrl2: DVec2, end: DVec2) -> CubicBezierSegment<f64> {
CubicBezierSegment {
from: Point::new(start.x, start.y),
ctrl1: Point::new(ctrl1.x, ctrl1.y),
ctrl2: Point::new(ctrl2.x, ctrl2.y),
to: Point::new(end.x, end.y),
}
}
#[derive(Clone)]
struct IntersectionSegment {
seg: PathSegment,
start_param: f64,
end_param: f64,
bounding_box: Aabb,
}
#[inline(never)]
fn subdivide_intersection_segment(int_seg: &IntersectionSegment) -> [IntersectionSegment; 2] {
let (seg0, seg1) = int_seg.seg.split_at(0.5);
let mid_param = (int_seg.start_param + int_seg.end_param) / 2.;
[
IntersectionSegment {
seg: seg0,
start_param: int_seg.start_param,
end_param: mid_param,
bounding_box: seg0.approx_bounding_box(),
},
IntersectionSegment {
seg: seg1,
start_param: mid_param,
end_param: int_seg.end_param,
bounding_box: seg1.approx_bounding_box(),
},
]
}
#[inline(never)]
fn path_segment_to_line_segment(seg: &PathSegment) -> [DVec2; 2] {
match seg {
PathSegment::Line(start, end) => [*start, *end],
PathSegment::Cubic(start, _, _, end) => [*start, *end],
PathSegment::Quadratic(start, _, end) => [*start, *end],
PathSegment::Arc(start, _, _, _, _, _, end) => [*start, *end],
}
}
#[inline(never)]
fn intersection_segments_overlap(seg0: &IntersectionSegment, seg1: &IntersectionSegment) -> bool {
match (&seg0.seg, &seg1.seg) {
(PathSegment::Line(start0, end0), PathSegment::Line(start1, end1)) => {
line_segments_intersect([*start0, *end0], [*start1, *end1], 1e-6) // TODO: configurable
}
(PathSegment::Line(start, end), _) => line_segment_aabb_intersect([*start, *end], &seg1.bounding_box),
(_, PathSegment::Line(start, end)) => line_segment_aabb_intersect([*start, *end], &seg0.bounding_box),
_ => bounding_boxes_overlap(&seg0.bounding_box, &seg1.bounding_box),
}
}
#[inline(never)]
pub fn segments_equal(seg0: &PathSegment, seg1: &PathSegment, point_epsilon: f64) -> bool {
match (*seg0, *seg1) {
(PathSegment::Line(start0, end0), PathSegment::Line(start1, end1)) => start0.abs_diff_eq(start1, point_epsilon) && end0.abs_diff_eq(end1, point_epsilon),
(PathSegment::Cubic(p00, p01, p02, p03), PathSegment::Cubic(p10, p11, p12, p13)) => {
let start_and_end_equal = p00.abs_diff_eq(p10, point_epsilon) && p03.abs_diff_eq(p13, point_epsilon);
let parameter_equal = p01.abs_diff_eq(p11, point_epsilon) && p02.abs_diff_eq(p12, point_epsilon);
let direction1 = seg0.sample_at(0.1);
let direction2 = seg1.sample_at(0.1);
let angles_equal = (direction1 - p00).angle_to(direction2 - p00).abs() < point_epsilon * 4.;
start_and_end_equal && (parameter_equal || angles_equal)
}
(PathSegment::Quadratic(p00, p01, p02), PathSegment::Quadratic(p10, p11, p12)) => {
p00.abs_diff_eq(p10, point_epsilon) && p01.abs_diff_eq(p11, point_epsilon) && p02.abs_diff_eq(p12, point_epsilon)
}
(PathSegment::Arc(p00, rx0, ry0, angle0, large_arc0, sweep0, p01), PathSegment::Arc(p10, rx1, ry1, angle1, large_arc1, sweep1, p11)) => {
p00.abs_diff_eq(p10, point_epsilon) &&
(rx0 - rx1).abs() < point_epsilon &&
(ry0 - ry1).abs() < point_epsilon &&
(angle0 - angle1).abs() < point_epsilon && // TODO: Phi can be anything if rx = ry. Also, handle rotations by Pi/2.
large_arc0 == large_arc1 &&
sweep0 == sweep1 &&
p01.abs_diff_eq(p11, point_epsilon)
}
_ => false,
}
}
pub fn path_segment_intersection(seg0: &PathSegment, seg1: &PathSegment, endpoints: bool, eps: &Epsilons) -> Vec<[f64; 2]> {
match (seg0, seg1) {
(PathSegment::Line(start0, end0), PathSegment::Line(start1, end1)) => {
if let Some(st) = line_segment_intersection([*start0, *end0], [*start1, *end1], eps.param) {
if !endpoints && (st.0 < eps.param || st.0 > 1. - eps.param) && (st.1 < eps.param || st.1 > 1. - eps.param) {
return vec![];
}
return vec![st.into()];
}
}
(PathSegment::Cubic(s1, c11, c21, e1), PathSegment::Cubic(s2, c12, c22, e2)) => {
let path1 = path_segment_cubic_to_lyon(*s1, *c11, *c21, *e1);
let path2 = path_segment_cubic_to_lyon(*s2, *c12, *c22, *e2);
let intersections = path1.cubic_intersections_t(&path2);
let intersections: Vec<_> = intersections.into_iter().map(|(s, t)| [s, t]).collect();
return intersections;
}
_ => (),
};
// Fallback for quadratics and arc segments
// https://math.stackexchange.com/questions/20321/how-can-i-tell-when-two-cubic-b%C3%A9zier-curves-intersect
let mut pairs = vec![(
IntersectionSegment {
seg: *seg0,
start_param: 0.,
end_param: 1.,
bounding_box: seg0.approx_bounding_box(),
},
IntersectionSegment {
seg: *seg1,
start_param: 0.,
end_param: 1.,
bounding_box: seg1.approx_bounding_box(),
},
)];
let mut next_pairs = Vec::new();
let mut params = Vec::new();
let mut subdivided0 = Vec::new();
let mut subdivided1 = Vec::new();
// Check if start and end points are on the other bezier curves. If so, add an intersection.
while !pairs.is_empty() {
next_pairs.clear();
if pairs.len() > 256 {
return calculate_overlap_intersections(seg0, seg1, eps);
}
for (seg0, seg1) in pairs.iter() {
if segments_equal(&seg0.seg, &seg1.seg, eps.point) {
// TODO: move this outside of this loop?
continue; // TODO: what to do?
}
let is_linear0 = bounding_box_max_extent(&seg0.bounding_box) <= eps.linear || (seg0.end_param - seg0.start_param).abs() < eps.param;
let is_linear1 = bounding_box_max_extent(&seg1.bounding_box) <= eps.linear || (seg1.end_param - seg1.start_param).abs() < eps.param;
if is_linear0 && is_linear1 {
let line_segment0 = path_segment_to_line_segment(&seg0.seg);
let line_segment1 = path_segment_to_line_segment(&seg1.seg);
if let Some(st) = line_segment_intersection(line_segment0, line_segment1, eps.param) {
params.push([lerp(seg0.start_param, seg0.end_param, st.0), lerp(seg1.start_param, seg1.end_param, st.1)]);
}
} else {
subdivided0.clear();
subdivided1.clear();
if is_linear0 {
subdivided0.push(seg0.clone())
} else {
subdivided0.extend_from_slice(&subdivide_intersection_segment(seg0))
};
if is_linear1 {
subdivided1.push(seg1.clone())
} else {
subdivided1.extend_from_slice(&subdivide_intersection_segment(seg1))
};
for seg0 in &subdivided0 {
for seg1 in &subdivided1 {
if intersection_segments_overlap(seg0, seg1) {
next_pairs.push((seg0.clone(), seg1.clone()));
}
}
}
}
}
std::mem::swap(&mut pairs, &mut next_pairs);
}
params
}
fn calculate_overlap_intersections(seg0: &PathSegment, seg1: &PathSegment, eps: &Epsilons) -> Vec<[f64; 2]> {
let start0 = seg0.start();
let end0 = seg0.end();
let start1 = seg1.start();
let end1 = seg1.end();
let mut intersections = Vec::new();
// Check start0 against seg1
if let Some(t1) = find_point_on_segment(seg1, start0, eps) {
intersections.push([0., t1]);
}
// Check end0 against seg1
if let Some(t1) = find_point_on_segment(seg1, end0, eps) {
intersections.push([1., t1]);
}
// Check start1 against seg0
if let Some(t0) = find_point_on_segment(seg0, start1, eps) {
intersections.push([t0, 0.]);
}
// Check end1 against seg0
if let Some(t0) = find_point_on_segment(seg0, end1, eps) {
intersections.push([t0, 1.]);
}
// Remove duplicates and sort intersections
intersections.sort_unstable_by(|a, b| a[0].partial_cmp(&b[0]).unwrap());
intersections.dedup_by(|a, b| DVec2::from(*a).abs_diff_eq(DVec2::from(*b), eps.param));
// Handle special cases
if intersections.is_empty() {
// Check if segments are identical
if (start0.abs_diff_eq(start1, eps.point)) && end0.abs_diff_eq(end1, eps.point) {
return vec![[0., 0.], [1., 1.]];
}
} else if intersections.len() > 2 {
// Keep only the first and last intersection points
intersections = vec![intersections[0], intersections[intersections.len() - 1]];
}
intersections
}
fn find_point_on_segment(seg: &PathSegment, point: DVec2, eps: &Epsilons) -> Option<f64> {
let start = 0.;
let end = 1.;
let mut t = 0.5;
for _ in 0..32 {
// Limit iterations to prevent infinite loops
let current_point = seg.sample_at(t);
if current_point.abs_diff_eq(point, eps.point) {
return Some(t);
}
let start_point = seg.sample_at(start);
let end_point = seg.sample_at(end);
let dist_start = (point - start_point).length_squared();
let dist_end = (point - end_point).length_squared();
let dist_current = (point - current_point).length_squared();
if dist_current < dist_start && dist_current < dist_end {
return Some(t);
}
if dist_start < dist_end {
t = (start + t) / 2.;
} else {
t = (t + end) / 2.;
}
if (end - start) < eps.param {
break;
}
}
None
}
#[cfg(test)]
mod test {
use super::*;
use glam::DVec2;
#[test]
fn intersect_cubic_slow_first() {
path_segment_intersection(&a(), &b(), true, &crate::EPS);
}
#[test]
fn intersect_cubic_slow_second() {
path_segment_intersection(&c(), &d(), true, &crate::EPS);
}
fn a() -> PathSegment {
PathSegment::Cubic(
DVec2::new(458.37027, 572.165771),
DVec2::new(428.525848, 486.720093),
DVec2::new(368.618805, 467.485992),
DVec2::new(273., 476.),
)
}
fn b() -> PathSegment {
PathSegment::Cubic(DVec2::new(273., 476.), DVec2::new(419., 463.), DVec2::new(481.741198, 514.692273), DVec2::new(481.333333, 768.))
}
fn c() -> PathSegment {
PathSegment::Cubic(DVec2::new(273., 476.), DVec2::new(107.564178, 490.730591), DVec2::new(161.737915, 383.575775), DVec2::new(0., 340.))
}
fn d() -> PathSegment {
PathSegment::Cubic(DVec2::new(0., 340.), DVec2::new(161.737914, 383.575765), DVec2::new(107.564182, 490.730587), DVec2::new(273., 476.))
}
}

View File

@ -1,29 +0,0 @@
use glam::DVec2;
pub type LineSegment = [DVec2; 2];
const COLLINEAR_EPS: f64 = f64::EPSILON * 64.;
#[inline(never)]
pub fn line_segment_intersection([p1, p2]: LineSegment, [p3, p4]: LineSegment, eps: f64) -> Option<(f64, f64)> {
// https://en.wikipedia.org/wiki/Intersection_(geometry)#Two_line_segments
let a = p2 - p1;
let b = p3 - p4;
let c = p3 - p1;
let denom = a.x * b.y - a.y * b.x;
if denom.abs() < COLLINEAR_EPS {
return None;
}
let s = (c.x * b.y - c.y * b.x) / denom;
let t = (a.x * c.y - a.y * c.x) / denom;
if (-eps..=1. + eps).contains(&s) && (-eps..=1. + eps).contains(&t) { Some((s, t)) } else { None }
}
pub fn line_segments_intersect(seg1: LineSegment, seg2: LineSegment, eps: f64) -> bool {
line_segment_intersection(seg1, seg2, eps).is_some()
}

View File

@ -1,89 +0,0 @@
use crate::aabb::Aabb;
use crate::line_segment::LineSegment;
const INSIDE: u8 = 0;
const LEFT: u8 = 1;
const RIGHT: u8 = 1 << 1;
const BOTTOM: u8 = 1 << 2;
const TOP: u8 = 1 << 3;
fn out_code(x: f64, y: f64, bounding_box: &Aabb) -> u8 {
let mut code = INSIDE;
if x < bounding_box.left() {
code |= LEFT;
} else if x > bounding_box.right() {
code |= RIGHT;
}
if y < bounding_box.top() {
code |= BOTTOM;
} else if y > bounding_box.bottom() {
code |= TOP;
}
code
}
pub(crate) fn line_segment_aabb_intersect(seg: LineSegment, bounding_box: &Aabb) -> bool {
let [mut p0, mut p1] = seg;
let mut outcode0 = out_code(p0.x, p0.y, bounding_box);
let mut outcode1 = out_code(p1.x, p1.y, bounding_box);
loop {
if (outcode0 | outcode1) == 0 {
// bitwise OR is 0: both points inside window; trivially accept and exit loop
return true;
} else if (outcode0 & outcode1) != 0 {
// bitwise AND is not 0: both points share an outside zone (LEFT, RIGHT, TOP,
// or BOTTOM), so both must be outside window; exit loop (accept is false)
return false;
} else {
// failed both tests, so calculate the line segment to clip
// from an outside point to an intersection with clip edge
let mut x = 0.;
let mut y = 0.;
// At least one endpoint is outside the clip rectangle; pick it.
let outcode_out = if outcode1 > outcode0 { outcode1 } else { outcode0 };
// Now find the intersection point;
// use formulas:
// slope = (y1 - y0) / (x1 - x0)
// x = x0 + (1 / slope) * (ym - y0), where ym is ymin or ymax
// y = y0 + slope * (xm - x0), where xm is xmin or xmax
// No need to worry about divide-by-zero because, in each case, the
// outcode bit being tested guarantees the denominator is non-zero
if (outcode_out & TOP) != 0 {
// point is above the clip window
x = p0.x + (p1.x - p0.x) * (bounding_box.bottom() - p0.y) / (p1.y - p0.y);
y = bounding_box.bottom();
} else if (outcode_out & BOTTOM) != 0 {
// point is below the clip window
x = p0.x + (p1.x - p0.x) * (bounding_box.top() - p0.y) / (p1.y - p0.y);
y = bounding_box.top();
} else if (outcode_out & RIGHT) != 0 {
// point is to the right of clip window
y = p0.y + (p1.y - p0.y) * (bounding_box.right() - p0.x) / (p1.x - p0.x);
x = bounding_box.right();
} else if (outcode_out & LEFT) != 0 {
// point is to the left of clip window
y = p0.y + (p1.y - p0.y) * (bounding_box.left() - p0.x) / (p1.x - p0.x);
x = bounding_box.left();
}
// Now we move outside point to intersection point to clip
// and get ready for next pass.
if outcode_out == outcode0 {
p0.x = x;
p0.y = y;
outcode0 = out_code(p0.x, p0.y, bounding_box);
} else {
p1.x = x;
p1.y = y;
outcode1 = out_code(p1.x, p1.y, bounding_box);
}
}
}
}

View File

@ -1,39 +0,0 @@
use crate::path_segment::PathSegment;
const EPS: f64 = 1e-12;
pub fn path_cubic_segment_self_intersection(seg: &PathSegment) -> Option<[f64; 2]> {
// https://math.stackexchange.com/questions/3931865/self-intersection-of-a-cubic-bezier-interpretation-of-the-solution
if let PathSegment::Cubic(p1, p2, p3, p4) = seg {
let ax = -p1.x + 3. * p2.x - 3. * p3.x + p4.x;
let ay = -p1.y + 3. * p2.y - 3. * p3.y + p4.y;
let bx = 3. * p1.x - 6. * p2.x + 3. * p3.x;
let by = 3. * p1.y - 6. * p2.y + 3. * p3.y;
let cx = -3. * p1.x + 3. * p2.x;
let cy = -3. * p1.y + 3. * p2.y;
let m = ay * bx - ax * by;
let n = ax * cy - ay * cx;
let k = (-3. * ax * ax * cy * cy + 6. * ax * ay * cx * cy + 4. * ax * bx * by * cy - 4. * ax * by * by * cx - 3. * ay * ay * cx * cx - 4. * ay * bx * bx * cy + 4. * ay * bx * by * cx)
/ (ax * ax * by * by - 2. * ax * ay * bx * by + ay * ay * bx * bx);
if k < 0. {
return None;
}
let t1 = (n / m + k.sqrt()) / 2.;
let t2 = (n / m - k.sqrt()) / 2.;
if (EPS..=1. - EPS).contains(&t1) && (EPS..=1. - EPS).contains(&t2) {
let mut result = [t1, t2];
result.sort_by(|a, b| a.partial_cmp(b).unwrap());
Some(result)
} else {
None
}
} else {
None
}
}

View File

@ -1,729 +0,0 @@
//! Defines the `PathSegment` enum and related functionality for representing and
//! manipulating path segments in 2D space.
//!
//! This module provides implementations for various types of path segments including
//! lines, cubic and quadratic Bézier curves, and elliptical arcs. It also includes
//! utility functions for operations such as bounding box calculation, segment splitting,
//! and arc-to-cubic conversion.
//!
//! The implementations in this module closely follow the SVG path specification,
//! making it suitable for use in vector graphics applications.
use crate::EPS;
use crate::aabb::{Aabb, bounding_box_around_point, expand_bounding_box, extend_bounding_box, merge_bounding_boxes};
use crate::math::{lerp, vector_angle};
use glam::{DMat2, DMat3, DVec2};
use std::f64::consts::{PI, TAU};
/// Represents a segment of a path in a 2D space, based on the SVG path specification.
///
/// This enum closely follows the path segment types defined in the SVG 2 specification.
/// For more details, see: <https://www.w3.org/TR/SVG2/paths.html>
///
/// Each variant of this enum corresponds to a different type of path segment:
/// - Line: A straight line between two points.
/// - Cubic: A cubic Bézier curve.
/// - Quadratic: A quadratic Bézier curve.
/// - Arc: An elliptical arc.
///
/// # Examples
///
/// Creating a line segment:
/// ```
/// use path_bool::PathSegment;
/// use glam::DVec2;
///
/// let line = PathSegment::Line(DVec2::new(0., 0.), DVec2::new(1., 1.));
/// ```
///
/// Creating a cubic Bézier curve:
/// ```
/// use path_bool::PathSegment;
/// use glam::DVec2;
///
/// let cubic = PathSegment::Cubic(
/// DVec2::new(0., 0.),
/// DVec2::new(1., 0.),
/// DVec2::new(1., 1.),
/// DVec2::new(2., 1.)
/// );
/// ```
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PathSegment {
/// A line segment from the first point to the second.
/// Corresponds to the SVG "L" command.
Line(DVec2, DVec2),
/// A cubic Bézier curve with start point, two control points, and end point.
/// Corresponds to the SVG "C" command.
Cubic(DVec2, DVec2, DVec2, DVec2),
/// A quadratic Bézier curve with start point, control point, and end point.
/// Corresponds to the SVG "Q" command.
Quadratic(DVec2, DVec2, DVec2),
/// An elliptical arc.
/// Corresponds to the SVG "A" command.
///
/// Parameters:
/// - Start point
/// - X-axis radius
/// - Y-axis radius
/// - X-axis rotation (in radians)
/// - Large arc flag (true if the arc should be greater than or equal to 180 degrees)
/// - Sweep flag (true if the arc should be drawn in a "positive-angle" direction)
/// - End point
Arc(DVec2, f64, f64, f64, bool, bool, DVec2),
}
impl PathSegment {
/// Calculates the angle of the tangent at the start point of the segment.
///
/// This method computes the angle (in radians) of the tangent vector at the
/// beginning of the path segment. The angle is measured clockwise
/// from the positive x-axis.
///
/// # Returns
///
/// A float representing the angle in radians, normalized to the range [0, 2π).
///
/// # Examples
///
/// ```
/// use path_bool::PathSegment;
/// use glam::DVec2;
/// use std::f64::consts::{TAU, FRAC_PI_4};
///
/// let line = PathSegment::Line(DVec2::new(0., 0.), DVec2::new(1., 1.));
/// assert_eq!(line.start_angle(), TAU - (FRAC_PI_4));
/// ```
pub fn start_angle(&self) -> f64 {
let angle = match *self {
PathSegment::Line(start, end) => (end - start).angle_to(DVec2::X),
PathSegment::Cubic(start, control1, control2, _) => {
let diff = control1 - start;
if diff.abs_diff_eq(DVec2::ZERO, EPS.point) {
// if this diff were empty too, the segments would have been converted to a line
(control2 - start).angle_to(DVec2::X)
} else {
diff.angle_to(DVec2::X)
}
}
// Apply same logic as for cubic bezier
PathSegment::Quadratic(start, control, _) => (control - start).to_angle(),
PathSegment::Arc(..) => self.arc_segment_to_cubics(0.001)[0].start_angle(),
};
use std::f64::consts::TAU;
(angle + TAU) % TAU
}
/// Computes the curvature at the start point of the segment.
///
/// The curvature is a measure of how sharply a curve bends. A straight line
/// has a curvature of 0, while a tight curve has a higher curvature value.
///
/// # Returns
///
/// A float representing the curvature. Positive values indicate a left
/// curve, while negative values indicate a right curve.
///
/// # Examples
///
/// ```
/// use path_bool::PathSegment;
/// use glam::DVec2;
///
/// let line = PathSegment::Line(DVec2::new(0., 0.), DVec2::new(1., 1.));
/// assert_eq!(line.start_curvature(), 0.);
///
/// let curve = PathSegment::Cubic(
/// DVec2::new(0., 0.),
/// DVec2::new(0., 1.),
/// DVec2::new(1., 1.),
/// DVec2::new(1., 0.)
/// );
/// assert!(curve.start_curvature() < 0.);
/// ```
pub fn start_curvature(&self) -> f64 {
match *self {
PathSegment::Line(_, _) => 0.,
PathSegment::Cubic(start, control1, control2, _) => {
let a = control1 - start;
let a = 3. * a;
let b = start - 2. * control1 + control2;
let b = 6. * b;
let numerator = a.x * b.y - a.y * b.x;
let denominator = a.length_squared() * a.length();
if denominator == 0. { 0. } else { numerator / denominator }
}
PathSegment::Quadratic(start, control, end) => {
// First derivative
let a = 2. * (control - start);
// Second derivative
let b = 2. * (start - 2. * control + end);
let numerator = a.x * b.y - a.y * b.x;
let denominator = a.length_squared() * a.length();
if denominator == 0. { 0. } else { numerator / denominator }
}
PathSegment::Arc(..) => self.arc_segment_to_cubics(0.001)[0].start_curvature(),
}
}
/// Converts the segment to a cubic Bézier curve representation.
///
/// This method provides a uniform representation of all segment types as
/// cubic Bézier curves. For segments that are not naturally cubic Bézier
/// curves (like lines or quadratic Bézier curves), an equivalent cubic
/// Bézier representation is computed.
///
/// # Returns
///
/// An array of four `DVec2` points representing the cubic Bézier curve:
/// [start point, first control point, second control point, end point]
///
/// # Examples
///
/// ```
/// use path_bool::PathSegment;
/// use glam::DVec2;
///
/// let line = PathSegment::Line(DVec2::new(0., 0.), DVec2::new(1., 1.));
/// let cubic = line.to_cubic();
/// assert_eq!(cubic[0], DVec2::new(0., 0.));
/// assert_eq!(cubic[3], DVec2::new(1., 1.));
/// ```
///
/// # Panics
///
/// This method is not implemented for `PathSegment::Arc`. Attempting to call
/// `to_cubic()` on an `Arc` segment will result in a panic.
pub fn to_cubic(&self) -> [DVec2; 4] {
match *self {
PathSegment::Line(start, end) => [start, start, end, end],
PathSegment::Cubic(s, c1, c2, e) => [s, c1, c2, e],
PathSegment::Quadratic(start, control, end) => {
// C0 = Q0
// C1 = Q0 + (2/3) (Q1 - Q0)
// C2 = Q2 + (2/3) (Q1 - Q2)
// C3 = Q2
let d1 = control - start;
let d2 = control - end;
[start, start + (2. / 3.) * d1, end + (2. / 3.) * d2, end]
}
PathSegment::Arc(..) => unimplemented!(),
}
}
#[must_use]
/// Retrieves the start point of a path segment.
pub fn start(&self) -> DVec2 {
match self {
PathSegment::Line(start, _) => *start,
PathSegment::Cubic(start, _, _, _) => *start,
PathSegment::Quadratic(start, _, _) => *start,
PathSegment::Arc(start, _, _, _, _, _, _) => *start,
}
}
#[must_use]
/// Retrieves the end point of a path segment.
pub fn end(&self) -> DVec2 {
match self {
PathSegment::Line(_, end) => *end,
PathSegment::Cubic(_, _, _, end) => *end,
PathSegment::Quadratic(_, _, end) => *end,
PathSegment::Arc(_, _, _, _, _, _, end) => *end,
}
}
#[must_use]
/// Reverses the direction of the path segment.
///
/// This method creates a new `PathSegment` that represents the same geometric shape
/// but in the opposite direction.
///
/// # Examples
///
/// ```
/// use path_bool::PathSegment;
/// use glam::DVec2;
///
/// let line = PathSegment::Line(DVec2::new(0., 0.), DVec2::new(1., 1.));
/// let reversed = line.reverse();
/// assert_eq!(reversed.start(), DVec2::new(1., 1.));
/// assert_eq!(reversed.end(), DVec2::new(0., 0.));
/// ```
pub fn reverse(&self) -> PathSegment {
match *self {
PathSegment::Line(start, end) => PathSegment::Line(end, start),
PathSegment::Cubic(p1, p2, p3, p4) => PathSegment::Cubic(p4, p3, p2, p1),
PathSegment::Quadratic(p1, p2, p3) => PathSegment::Quadratic(p3, p2, p1),
PathSegment::Arc(start, rx, ry, phi, fa, fs, end) => PathSegment::Arc(end, rx, ry, phi, fa, !fs, start),
}
}
#[must_use]
/// Converts an arc segment to its center parameterization.
///
/// This method is only meaningful for `Arc` segments. For other segment types,
/// it returns `None`.
///
/// # Returns
///
/// An `Option` containing `PathArcSegmentCenterParametrization` if the segment
/// is an `Arc`, or `None` otherwise.
pub fn arc_segment_to_center(&self) -> Option<PathArcSegmentCenterParametrization> {
if let PathSegment::Arc(xy1, rx, ry, phi, fa, fs, xy2) = *self {
if rx == 0. || ry == 0. {
return None;
}
let rotation_matrix = DMat2::from_angle(-phi.to_radians());
let xy1_prime = rotation_matrix * (xy1 - xy2) * 0.5;
let mut rx2 = rx * rx;
let mut ry2 = ry * ry;
let x1_prime2 = xy1_prime.x * xy1_prime.x;
let y1_prime2 = xy1_prime.y * xy1_prime.y;
let mut rx = rx.abs();
let mut ry = ry.abs();
let lambda = x1_prime2 / rx2 + y1_prime2 / ry2 + 1e-12;
if lambda > 1. {
let lambda_sqrt = lambda.sqrt();
rx *= lambda_sqrt;
ry *= lambda_sqrt;
let lambda_abs = lambda.abs();
rx2 *= lambda_abs;
ry2 *= lambda_abs;
}
let sign = if fa == fs { -1. } else { 1. };
let multiplier = ((rx2 * ry2 - rx2 * y1_prime2 - ry2 * x1_prime2) / (rx2 * y1_prime2 + ry2 * x1_prime2)).sqrt();
let cx_prime = sign * multiplier * ((rx * xy1_prime.y) / ry);
let cy_prime = sign * multiplier * ((-ry * xy1_prime.x) / rx);
let cxy = rotation_matrix.transpose() * DVec2::new(cx_prime, cy_prime) + (xy1 + xy2) * 0.5;
let vec1 = DVec2::new((xy1_prime.x - cx_prime) / rx, (xy1_prime.y - cy_prime) / ry);
let theta1 = vector_angle(DVec2::new(1., 0.), vec1);
let mut delta_theta = vector_angle(vec1, DVec2::new((-xy1_prime.x - cx_prime) / rx, (-xy1_prime.y - cy_prime) / ry));
if !fs && delta_theta > 0. {
delta_theta -= TAU;
} else if fs && delta_theta < 0. {
delta_theta += TAU;
}
Some(PathArcSegmentCenterParametrization {
center: cxy,
theta1,
delta_theta,
rx,
ry,
phi,
})
} else {
None
}
}
#[must_use]
/// Samples a point on the path segment at a given parameter value.
///
/// # Arguments
///
/// * `t` - A value between 0. and 1. representing the position along the segment.
///
/// # Examples
///
/// ```
/// use path_bool::PathSegment;
/// use glam::DVec2;
///
/// let line = PathSegment::Line(DVec2::new(0., 0.), DVec2::new(2., 2.));
/// assert_eq!(line.sample_at(0.5), DVec2::new(1., 1.));
/// ```
pub fn sample_at(&self, t: f64) -> DVec2 {
match *self {
PathSegment::Line(start, end) => start.lerp(end, t),
PathSegment::Cubic(p1, p2, p3, p4) => {
let p01 = p1.lerp(p2, t);
let p12 = p2.lerp(p3, t);
let p23 = p3.lerp(p4, t);
let p012 = p01.lerp(p12, t);
let p123 = p12.lerp(p23, t);
p012.lerp(p123, t)
}
PathSegment::Quadratic(p1, p2, p3) => {
let p01 = p1.lerp(p2, t);
let p12 = p2.lerp(p3, t);
p01.lerp(p12, t)
}
PathSegment::Arc(start, rx, ry, phi, _, _, end) => {
if let Some(center_param) = self.arc_segment_to_center() {
let theta = center_param.theta1 + t * center_param.delta_theta;
let p = DVec2::new(rx * theta.cos(), ry * theta.sin());
let rotation_matrix = DMat2::from_angle(phi);
rotation_matrix * p + center_param.center
} else {
start.lerp(end, t)
}
}
}
}
#[must_use]
/// Approximates an arc segment with a series of cubic Bézier curves.
///
/// This method is primarily used for `Arc` segments, converting them into
/// a series of cubic Bézier curves for easier rendering or manipulation.
/// For non-`Arc` segments, it returns a vector containing only the original segment.
///
/// # Arguments
///
/// * `max_delta_theta` - The maximum angle (in radians) that each cubic Bézier
/// curve approximation should span.
///
/// # Returns
///
/// A vector of `PathSegment::Cubic` approximating the original segment.
pub fn arc_segment_to_cubics(&self, max_delta_theta: f64) -> Vec<PathSegment> {
if let PathSegment::Arc(start, rx, ry, phi, _, _, end) = *self {
if let Some(center_param) = self.arc_segment_to_center() {
let count = ((center_param.delta_theta.abs() / max_delta_theta).ceil() as usize).max(1);
let from_unit = DMat3::from_translation(center_param.center) * DMat3::from_angle(phi.to_radians()) * DMat3::from_scale(DVec2::new(rx, ry));
let theta = center_param.delta_theta / count as f64;
let k = (4. / 3.) * (theta / 4.).tan();
let sin_theta = theta.sin();
let cos_theta = theta.cos();
(0..count)
.map(|i| {
let start = DVec2::new(1., 0.);
let control1 = DVec2::new(1., k);
let control2 = DVec2::new(cos_theta + k * sin_theta, sin_theta - k * cos_theta);
let end = DVec2::new(cos_theta, sin_theta);
let matrix = DMat3::from_angle(center_param.theta1 + i as f64 * theta) * from_unit;
let start = (matrix * start.extend(1.)).truncate();
let control1 = (matrix * control1.extend(1.)).truncate();
let control2 = (matrix * control2.extend(1.)).truncate();
let end = (matrix * end.extend(1.)).truncate();
PathSegment::Cubic(start, control1, control2, end)
})
.collect()
} else {
vec![PathSegment::Line(start, end)]
}
} else {
vec![*self]
}
}
}
/// Represents the center parameterization of an elliptical arc.
///
/// This struct is used internally to perform calculations on arc segments.
pub struct PathArcSegmentCenterParametrization {
center: DVec2,
theta1: f64,
delta_theta: f64,
rx: f64,
ry: f64,
phi: f64,
}
/// Converts the center parameterization back to an arc segment.
///
/// # Arguments
///
/// * `start` - Optional start point of the arc. If `None`, the start point is calculated.
/// * `end` - Optional end point of the arc. If `None`, the end point is calculated.
///
/// # Returns
///
/// A `PathSegment::Arc` representing the arc described by this parameterization.
impl PathArcSegmentCenterParametrization {
#[must_use]
pub fn arc_segment_from_center(&self, start: Option<DVec2>, end: Option<DVec2>) -> PathSegment {
let rotation_matrix = DMat2::from_angle(self.phi);
let mut xy1 = rotation_matrix * DVec2::new(self.rx * self.theta1.cos(), self.ry * self.theta1.sin()) + self.center;
let mut xy2 = rotation_matrix * DVec2::new(self.rx * (self.theta1 + self.delta_theta).cos(), self.ry * (self.theta1 + self.delta_theta).sin()) + self.center;
let fa = self.delta_theta.abs() > PI;
let fs = self.delta_theta > 0.;
xy1 = start.unwrap_or(xy1);
xy2 = end.unwrap_or(xy2);
PathSegment::Arc(xy1, self.rx, self.ry, self.phi, fa, fs, xy2)
}
}
/// Evaluates a 1D cubic Bézier curve at a given parameter value.
///
/// # Arguments
///
/// * `p0`, `p1`, `p2`, `p3` - Control points of the cubic Bézier curve.
/// * `t` - Parameter value between 0 and 1.
///
/// # Returns
///
/// The value of the Bézier curve at parameter `t`.
fn eval_cubic_1d(p0: f64, p1: f64, p2: f64, p3: f64, t: f64) -> f64 {
let p01 = lerp(p0, p1, t);
let p12 = lerp(p1, p2, t);
let p23 = lerp(p2, p3, t);
let p012 = lerp(p01, p12, t);
let p123 = lerp(p12, p23, t);
lerp(p012, p123, t)
}
/// Computes the bounding interval of a 1D cubic Bézier curve.
///
/// This function finds the minimum and maximum values of a cubic Bézier curve
/// over the interval [0, 1].
///
/// # Arguments
///
/// * `p0`, `p1`, `p2`, `p3` - Control points of the cubic Bézier curve.
///
/// # Returns
///
/// A tuple `(min, max)` representing the bounding interval.
fn cubic_bounding_interval(p0: f64, p1: f64, p2: f64, p3: f64) -> (f64, f64) {
let mut min = p0.min(p3);
let mut max = p0.max(p3);
let a = 3. * (-p0 + 3. * p1 - 3. * p2 + p3);
let b = 6. * (p0 - 2. * p1 + p2);
let c = 3. * (p1 - p0);
let d = b * b - 4. * a * c;
if d < 0. || a == 0. {
// TODO: if a=0, solve linear
return (min, max);
}
let sqrt_d = d.sqrt();
let t0 = (-b - sqrt_d) / (2. * a);
if 0. < t0 && t0 < 1. {
let x0 = eval_cubic_1d(p0, p1, p2, p3, t0);
min = min.min(x0);
max = max.max(x0);
}
let t1 = (-b + sqrt_d) / (2. * a);
if 0. < t1 && t1 < 1. {
let x1 = eval_cubic_1d(p0, p1, p2, p3, t1);
min = min.min(x1);
max = max.max(x1);
}
(min, max)
}
/// Evaluates a 1D quadratic Bézier curve at a given parameter value.
///
/// # Arguments
///
/// * `p0`, `p1`, `p2` - Control points of the quadratic Bézier curve.
/// * `t` - Parameter value between 0 and 1.
///
/// # Returns
///
/// The value of the Bézier curve at parameter `t`.
fn eval_quadratic_1d(p0: f64, p1: f64, p2: f64, t: f64) -> f64 {
let p01 = lerp(p0, p1, t);
let p12 = lerp(p1, p2, t);
lerp(p01, p12, t)
}
/// Computes the bounding interval of a 1D quadratic Bézier curve.
///
/// This function finds the minimum and maximum values of a quadratic Bézier curve
/// over the interval [0, 1].
///
/// # Arguments
///
/// * `p0`, `p1`, `p2` - Control points of the quadratic Bézier curve.
///
/// # Returns
///
/// A tuple `(min, max)` representing the bounding interval.
fn quadratic_bounding_interval(p0: f64, p1: f64, p2: f64) -> (f64, f64) {
let mut min = p0.min(p2);
let mut max = p0.max(p2);
let denominator = p0 - 2. * p1 + p2;
if denominator == 0. {
return (min, max);
}
let t = (p0 - p1) / denominator;
if (0.0..=1.).contains(&t) {
let x = eval_quadratic_1d(p0, p1, p2, t);
min = min.min(x);
max = max.max(x);
}
(min, max)
}
fn in_interval(x: f64, x0: f64, x1: f64) -> bool {
(x0..=x1).contains(&x)
}
impl PathSegment {
/// Computes the bounding box of the path segment.
///
/// # Returns
///
/// An [`Aabb`] representing the axis-aligned bounding box of the segment.
pub(crate) fn bounding_box(&self) -> Aabb {
match *self {
PathSegment::Line(start, end) => Aabb::new(start.x.min(end.x), start.y.min(end.y), start.x.max(end.x), start.y.max(end.y)),
PathSegment::Cubic(p1, p2, p3, p4) => {
let (left, right) = cubic_bounding_interval(p1.x, p2.x, p3.x, p4.x);
let (top, bottom) = cubic_bounding_interval(p1.y, p2.y, p3.y, p4.y);
Aabb::new(left, top, right, bottom)
}
PathSegment::Quadratic(p1, p2, p3) => {
let (left, right) = quadratic_bounding_interval(p1.x, p2.x, p3.x);
let (top, bottom) = quadratic_bounding_interval(p1.y, p2.y, p3.y);
Aabb::new(left, top, right, bottom)
}
PathSegment::Arc(start, rx, ry, phi, _, _, end) => {
if let Some(center_param) = self.arc_segment_to_center() {
let theta2 = center_param.theta1 + center_param.delta_theta;
let mut bounding_box = extend_bounding_box(Some(bounding_box_around_point(start, 0.)), end);
if phi == 0. || rx == ry {
// TODO: Fix the fact that the following gives false positives, resulting in larger boxes
if in_interval(-PI, center_param.theta1, theta2) || in_interval(PI, center_param.theta1, theta2) {
bounding_box = extend_bounding_box(Some(bounding_box), DVec2::new(center_param.center.x - rx, center_param.center.y));
}
if in_interval(-PI / 2., center_param.theta1, theta2) || in_interval(3. * PI / 2., center_param.theta1, theta2) {
bounding_box = extend_bounding_box(Some(bounding_box), DVec2::new(center_param.center.x, center_param.center.y - ry));
}
if in_interval(0., center_param.theta1, theta2) || in_interval(2. * PI, center_param.theta1, theta2) {
bounding_box = extend_bounding_box(Some(bounding_box), DVec2::new(center_param.center.x + rx, center_param.center.y));
}
if in_interval(PI / 2., center_param.theta1, theta2) || in_interval(5. * PI / 2., center_param.theta1, theta2) {
bounding_box = extend_bounding_box(Some(bounding_box), DVec2::new(center_param.center.x, center_param.center.y + ry));
}
expand_bounding_box(&bounding_box, 1e-11) // TODO: Get rid of expansion
} else {
// TODO: Don't convert to cubics
let cubics = self.arc_segment_to_cubics(PI / 16.);
let mut bounding_box = bounding_box_around_point(start, 0.);
for cubic_seg in cubics {
bounding_box = merge_bounding_boxes(&bounding_box, &cubic_seg.bounding_box());
}
bounding_box
}
} else {
extend_bounding_box(Some(bounding_box_around_point(start, 0.)), end)
}
}
}
}
/// Computes a loose bounding box that surrounds all anchors, but also the handles of cubic and quadratic segments.
/// This will usually be larger than the actual bounding box, but is faster to compute because it does not have to find where each curve reaches its maximum and minimum.
pub(crate) fn approx_bounding_box(&self) -> Aabb {
match *self {
PathSegment::Cubic(p1, p2, p3, p4) => {
// Use the control points to create a bounding box
let left = p1.x.min(p2.x).min(p3.x).min(p4.x);
let right = p1.x.max(p2.x).max(p3.x).max(p4.x);
let top = p1.y.min(p2.y).min(p3.y).min(p4.y);
let bottom = p1.y.max(p2.y).max(p3.y).max(p4.y);
Aabb::new(left, top, right, bottom)
}
PathSegment::Quadratic(p1, p2, p3) => {
// Use the control points to create a bounding box
let left = p1.x.min(p2.x).min(p3.x);
let right = p1.x.max(p2.x).max(p3.x);
let top = p1.y.min(p2.y).min(p3.y);
let bottom = p1.y.max(p2.y).max(p3.y);
Aabb::new(left, top, right, bottom)
}
seg => seg.bounding_box(),
}
}
/// Splits the path segment at a given parameter value.
///
/// # Arguments
///
/// * `t` - A value between 0. and 1. representing the split point along the segment.
///
/// # Returns
///
/// A tuple of two `PathSegment`s representing the parts before and after the split point.
///
/// # Examples
///
/// ```
/// use path_bool::PathSegment;
/// use glam::DVec2;
///
/// let line = PathSegment::Line(DVec2::new(0., 0.), DVec2::new(2., 2.));
/// let (first_half, second_half) = line.split_at(0.5);
/// assert_eq!(first_half.end(), DVec2::new(1., 1.));
/// assert_eq!(second_half.start(), DVec2::new(1., 1.));
/// ```
pub fn split_at(&self, t: f64) -> (PathSegment, PathSegment) {
match *self {
PathSegment::Line(start, end) => {
let p = start.lerp(end, t);
(PathSegment::Line(start, p), PathSegment::Line(p, end))
}
PathSegment::Cubic(p0, p1, p2, p3) => {
let p01 = p0.lerp(p1, t);
let p12 = p1.lerp(p2, t);
let p23 = p2.lerp(p3, t);
let p012 = p01.lerp(p12, t);
let p123 = p12.lerp(p23, t);
let p = p012.lerp(p123, t);
(PathSegment::Cubic(p0, p01, p012, p), PathSegment::Cubic(p, p123, p23, p3))
}
PathSegment::Quadratic(p0, p1, p2) => {
let p01 = p0.lerp(p1, t);
let p12 = p1.lerp(p2, t);
let p = p01.lerp(p12, t);
(PathSegment::Quadratic(p0, p01, p), PathSegment::Quadratic(p, p12, p2))
}
PathSegment::Arc(start, _, _, _, _, _, end) => {
if let Some(center_param) = self.arc_segment_to_center() {
let mid_delta_theta = center_param.delta_theta * t;
let seg1 = PathArcSegmentCenterParametrization {
delta_theta: mid_delta_theta,
..center_param
}
.arc_segment_from_center(Some(start), None);
let seg2 = PathArcSegmentCenterParametrization {
theta1: center_param.theta1 + mid_delta_theta,
delta_theta: center_param.delta_theta - mid_delta_theta,
..center_param
}
.arc_segment_from_center(None, Some(end));
(seg1, seg2)
} else {
// https://svgwg.org/svg2-draft/implnote.html#ArcCorrectionOutOfRangeRadii
let p = start.lerp(end, t);
(PathSegment::Line(start, p), PathSegment::Line(p, end))
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,92 +0,0 @@
use glam::{BVec2, DVec2};
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct Aabb {
min: DVec2,
max: DVec2,
}
impl Default for Aabb {
fn default() -> Self {
Self {
min: DVec2::INFINITY,
max: DVec2::NEG_INFINITY,
}
}
}
impl Aabb {
#[inline]
pub(crate) fn min(&self) -> DVec2 {
self.min
}
#[inline]
pub(crate) fn max(&self) -> DVec2 {
self.max
}
pub(crate) const fn new(left: f64, top: f64, right: f64, bottom: f64) -> Self {
Aabb {
min: DVec2::new(left, top),
max: DVec2::new(right, bottom),
}
}
#[inline]
pub(crate) fn top(&self) -> f64 {
self.min.y
}
#[inline]
pub(crate) fn left(&self) -> f64 {
self.min.x
}
#[inline]
pub(crate) fn right(&self) -> f64 {
self.max.x
}
#[inline]
pub(crate) fn bottom(&self) -> f64 {
self.max.y
}
}
#[inline]
pub(crate) fn bounding_boxes_overlap(a: &Aabb, b: &Aabb) -> bool {
(a.min.cmple(b.max) & b.min.cmple(a.max)) == BVec2::TRUE
}
#[inline]
pub(crate) fn merge_bounding_boxes(a: &Aabb, b: &Aabb) -> Aabb {
Aabb {
min: a.min.min(b.min),
max: a.max.max(b.max),
}
}
#[inline]
pub(crate) fn extend_bounding_box(bounding_box: Option<Aabb>, point: DVec2) -> Aabb {
match bounding_box {
Some(bb) => Aabb {
min: bb.min.min(point),
max: bb.max.max(point),
},
None => Aabb { min: point, max: point },
}
}
pub(crate) fn bounding_box_max_extent(bounding_box: &Aabb) -> f64 {
(bounding_box.max - bounding_box.min).max_element()
}
pub(crate) fn bounding_box_around_point(point: DVec2, padding: f64) -> Aabb {
Aabb {
min: point - DVec2::splat(padding),
max: point + DVec2::splat(padding),
}
}
pub(crate) fn expand_bounding_box(bounding_box: &Aabb, padding: f64) -> Aabb {
Aabb {
min: bounding_box.min - DVec2::splat(padding),
max: bounding_box.max + DVec2::splat(padding),
}
}

View File

@ -1,6 +0,0 @@
#[derive(Clone, Copy, Debug)]
pub struct Epsilons {
pub point: f64,
pub linear: f64,
pub param: f64,
}

View File

@ -1,128 +0,0 @@
use crate::aabb::Aabb;
use glam::{DVec2, IVec2};
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
pub(crate) struct Grid {
cell_factor: f64,
cells: FxHashMap<IVec2, SmallVec<[usize; 6]>>,
}
impl Grid {
pub(crate) fn new(cell_size: f64, edges: usize) -> Self {
Grid {
cell_factor: cell_size.recip(),
cells: FxHashMap::with_capacity_and_hasher(edges, Default::default()),
}
}
pub(crate) fn insert(&mut self, bbox: &Aabb, index: usize) {
let min_cell = self.point_to_cell_floor(bbox.min());
let max_cell = self.point_to_cell_ceil(bbox.max());
for i in min_cell.x..=max_cell.x {
for j in min_cell.y..=max_cell.y {
self.cells.entry((i, j).into()).or_default().push(index);
}
}
}
pub(crate) fn query(&self, bbox: &Aabb, result: &mut BitVec) {
let min_cell = self.point_to_cell_floor(bbox.min());
let max_cell = self.point_to_cell_ceil(bbox.max());
for i in min_cell.x..=max_cell.x {
for j in min_cell.y..=max_cell.y {
if let Some(indices) = self.cells.get(&(i, j).into()) {
for &index in indices {
result.set(index);
}
}
}
}
// result.sort_unstable();
// result.dedup();
}
fn point_to_cell_ceil(&self, point: DVec2) -> IVec2 {
(point * self.cell_factor).ceil().as_ivec2()
}
fn point_to_cell_floor(&self, point: DVec2) -> IVec2 {
(point * self.cell_factor).floor().as_ivec2()
}
}
pub struct BitVec {
data: Vec<u64>,
}
impl BitVec {
pub fn new(capacity: usize) -> Self {
let num_words = capacity.div_ceil(64);
BitVec { data: vec![0; num_words] }
}
pub fn set(&mut self, index: usize) {
let word_index = index / 64;
let bit_index = index % 64;
self.data[word_index] |= 1u64 << bit_index;
}
pub fn clear(&mut self) {
self.data.fill(0);
}
pub fn iter_set_bits(&self) -> BitVecIterator<'_> {
BitVecIterator {
bit_vec: self,
current_word: self.data[0],
word_index: 0,
}
}
}
pub struct BitVecIterator<'a> {
bit_vec: &'a BitVec,
current_word: u64,
word_index: usize,
}
impl<'a> Iterator for BitVecIterator<'a> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.current_word == 0 {
self.word_index += 1;
if self.word_index == self.bit_vec.data.len() {
return None;
}
self.current_word = self.bit_vec.data[self.word_index];
continue;
}
let tz = self.current_word.trailing_zeros() as usize;
self.current_word ^= 1 << tz;
let result = self.word_index * 64 + tz;
return Some(result);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bitvec() {
let mut bv = BitVec::new(200);
bv.set(5);
bv.set(64);
bv.set(128);
bv.set(199);
let set_bits: Vec<usize> = bv.iter_set_bits().collect();
assert_eq!(set_bits, vec![5, 64, 128, 199]);
}
}

View File

@ -1,23 +0,0 @@
use glam::{DVec2, FloatExt};
pub use std::f64::consts::PI;
pub fn lin_map(value: f64, in_min: f64, in_max: f64, out_min: f64, out_max: f64) -> f64 {
((value - in_min) / (in_max - in_min)) * (out_max - out_min) + out_min
}
pub fn lerp(a: f64, b: f64, t: f64) -> f64 {
a.lerp(b, t)
}
pub fn vector_angle(u: DVec2, v: DVec2) -> f64 {
const EPS: f64 = 1e-12;
let sign = u.x * v.y - u.y * v.x;
if sign.abs() < EPS && (u + v).length_squared() < EPS * EPS {
// TODO: `u` can be scaled
return PI;
}
sign.signum() * (u.dot(v) / (u.length() * v.length())).acos()
}

View File

@ -1,194 +0,0 @@
use crate::path_boolean::{self, FillRule, PathBooleanOperation};
use crate::path_data::{path_from_path_data, path_to_path_data};
use core::panic;
use glob::glob;
use image::{DynamicImage, GenericImageView, RgbaImage};
use resvg::render;
use resvg::tiny_skia::Transform;
use resvg::usvg::{Options, Tree};
use std::fs;
use std::path::PathBuf;
use svg::parser::Event;
const TOLERANCE: u8 = 84;
fn get_fill_rule(fill_rule: &str) -> FillRule {
match fill_rule {
"evenodd" => FillRule::EvenOdd,
_ => FillRule::NonZero,
}
}
#[test]
fn visual_tests() {
let ops = [
("union", PathBooleanOperation::Union),
("difference", PathBooleanOperation::Difference),
("intersection", PathBooleanOperation::Intersection),
("exclusion", PathBooleanOperation::Exclusion),
("division", PathBooleanOperation::Division),
("fracture", PathBooleanOperation::Fracture),
];
let folders: Vec<(String, PathBuf, &str, PathBooleanOperation)> = glob("visual-tests/*/")
.expect("Failed to read glob pattern")
.flat_map(|entry| {
let dir = entry.expect("Failed to get directory entry");
ops.iter()
.map(move |(op_name, op)| (dir.file_name().unwrap().to_string_lossy().into_owned(), dir.clone(), *op_name, *op))
})
.collect();
let mut failure = false;
for (name, dir, op_name, op) in folders {
let test_name = format!("{} {}", name, op_name);
println!("Running test: {}", test_name);
fs::create_dir_all(dir.join("test-results")).expect("Failed to create test-results directory");
let original_path = dir.join("original.svg");
let mut content = String::new();
let svg_tree = svg::open(&original_path, &mut content).expect("Failed to parse SVG");
let mut paths = Vec::new();
let mut first_path_attributes = String::new();
let mut width = String::new();
let mut height = String::new();
let mut view_box = String::new();
let mut transform = String::new();
for event in svg_tree {
match event {
Event::Tag("svg", svg::node::element::tag::Type::Start, attributes) => {
width = attributes.get("width").map(|s| s.to_string()).unwrap_or_default();
height = attributes.get("height").map(|s| s.to_string()).unwrap_or_default();
view_box = attributes.get("viewBox").map(|s| s.to_string()).unwrap_or_default();
}
Event::Tag("g", svg::node::element::tag::Type::Start, attributes) => {
if let Some(transform_attr) = attributes.get("transform") {
transform = transform_attr.to_string();
}
}
Event::Tag("path", svg::node::element::tag::Type::Empty, attributes) => {
let data = attributes.get("d").map(|s| s.to_string()).expect("Path data not found");
let fill_rule = attributes.get("fill-rule").map(|v| v.to_string()).unwrap_or_else(|| "nonzero".to_string());
paths.push((data, fill_rule));
// Store attributes of the first path
if first_path_attributes.is_empty() {
for (key, value) in attributes.iter() {
if key != "d" && key != "id" {
first_path_attributes.push_str(&format!("{}=\"{}\" ", key, value));
}
}
}
}
_ => {}
}
}
if (width.is_empty() || height.is_empty()) && !view_box.is_empty() {
let vb: Vec<&str> = view_box.split_whitespace().collect();
if vb.len() == 4 {
width = vb[2].to_string();
height = vb[3].to_string();
}
}
if width.is_empty() || height.is_empty() {
panic!("Failed to extract width and height from SVG");
}
let a_node = paths[0].clone();
let b_node = paths[1].clone();
let a = path_from_path_data(&a_node.0).unwrap();
let b = path_from_path_data(&b_node.0).unwrap();
let a_fill_rule = get_fill_rule(&a_node.1);
let b_fill_rule = get_fill_rule(&b_node.1);
let result = path_boolean::path_boolean(&a, a_fill_rule, &b, b_fill_rule, op).unwrap();
// Create the result SVG with correct dimensions and transform
let mut result_svg = format!("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{}\" height=\"{}\" viewBox=\"{}\">", width, height, view_box);
if !transform.is_empty() {
result_svg.push_str(&format!("<g transform=\"{}\">", transform));
}
for path in &result {
result_svg.push_str(&format!("<path d=\"{}\" {}/>", path_to_path_data(path, 1e-4), first_path_attributes));
}
if !transform.is_empty() {
result_svg.push_str("</g>");
}
result_svg.push_str("</svg>");
// Save the result SVG
let destination_path = dir.join("test-results").join(format!("{}-ours.svg", op_name));
fs::write(&destination_path, &result_svg).expect("Failed to write result SVG");
// Render and compare images
let ground_truth_path = dir.join(format!("{}.svg", op_name));
let ground_truth_svg = fs::read_to_string(&ground_truth_path).expect("Failed to read ground truth SVG");
let ours_image = render_svg(&result_svg);
let ground_truth_image = render_svg(&ground_truth_svg);
let ours_png_path = dir.join("test-results").join(format!("{}-ours.png", op_name));
ours_image.save(&ours_png_path).expect("Failed to save our PNG");
let ground_truth_png_path = dir.join("test-results").join(format!("{}.png", op_name));
ground_truth_image.save(&ground_truth_png_path).expect("Failed to save ground truth PNG");
failure |= compare_images(&ours_image, &ground_truth_image, TOLERANCE);
// Check the number of paths
let result_path_count = result.len();
let ground_truth_path_count = ground_truth_svg.matches("<path").count();
if result_path_count != ground_truth_path_count {
failure = true;
eprintln!("Number of paths doesn't match for test: {}", test_name);
}
}
if failure {
panic!("Some tests have failed");
}
}
fn render_svg(svg_code: &str) -> DynamicImage {
let opts = Options::default();
let tree = Tree::from_str(svg_code, &opts).unwrap();
let pixmap_size = tree.size();
let (width, height) = (pixmap_size.width() as u32, pixmap_size.height() as u32);
let mut pixmap = resvg::tiny_skia::Pixmap::new(width, height).unwrap();
let mut pixmap_mut = pixmap.as_mut();
render(&tree, Transform::default(), &mut pixmap_mut);
DynamicImage::ImageRgba8(RgbaImage::from_raw(width, height, pixmap.data().to_vec()).unwrap())
}
fn compare_images(img1: &DynamicImage, img2: &DynamicImage, tolerance: u8) -> bool {
assert_eq!(img1.dimensions(), img2.dimensions(), "Image dimensions do not match");
for (x, y, pixel1) in img1.pixels() {
let pixel2 = img2.get_pixel(x, y);
for i in 0..4 {
let difference = (pixel1[i] as i32 - pixel2[i] as i32).unsigned_abs() as u8;
if difference > tolerance {
println!("Difference {} larger than tolerance {} at [{}, {}], channel {}.", difference, tolerance, x, y, i);
return true;
}
assert!(
difference <= tolerance,
"Difference {} larger than tolerance {} at [{}, {}], channel {}.",
difference,
tolerance,
x,
y,
i
);
}
}
false
}

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 26.811783335962,37.120515321746 A 8.683698700000 8.683698700000 0.000000000000 0 1 21.505442000000,29.120510000000 A 8.683698700000 8.683698700000 0.000000000000 0 1 27.251230394932,20.948893816238 A 12.347054000000 12.347054000000 0.000000000000 0 0 17.706438000000,16.434252000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 5.359384500000,28.781305000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 17.706438000000,41.128359000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 26.811783336064,37.120515321723"/>
</svg>

Before

Width:  |  Height:  |  Size: 889 B

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 26.811783336064,37.120515321723 A 12.347054000000 12.347054000000 0.000000000000 0 0 30.053492000000,28.781305000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 27.251230395016,20.948893816326 A 8.683698700000 8.683698700000 0.000000000000 0 0 21.505442000000,29.120509958794 A 8.683698700000 8.683698700000 0.000000000000 0 0 26.811783335962,37.120515321746"/><path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 26.811783335962,37.120515321746 A 8.683698700000 8.683698700000 0.000000000000 0 1 21.505442000000,29.120510000000 A 8.683698700000 8.683698700000 0.000000000000 0 1 27.251230394932,20.948893816238 A 12.347054000000 12.347054000000 0.000000000000 0 0 17.706438000000,16.434252000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 5.359384500000,28.781305000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 17.706438000000,41.128359000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 26.811783336064,37.120515321723"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 26.811783335962,37.120515321746 A 8.683698700000 8.683698700000 0.000000000000 0 0 30.189140000000,37.804209000000 A 8.683698700000 8.683698700000 0.000000000000 0 0 38.872839000000,29.120510000000 A 8.683698700000 8.683698700000 0.000000000000 0 0 30.189140000000,20.436811000000 A 8.683698700000 8.683698700000 0.000000000000 0 0 27.251230394932,20.948893816238 A 12.347054000000 12.347054000000 0.000000000000 0 1 30.053492000000,28.781305000000 A 12.347054000000 12.347054000000 0.000000000000 0 1 26.811783336064,37.120515321723 A 8.683698700000 8.683698700000 0.000000000000 0 1 21.505442000000,29.120510000000 A 8.683698700000 8.683698700000 0.000000000000 0 1 27.251230394932,20.948893816238 A 12.347054000000 12.347054000000 0.000000000000 0 0 17.706438000000,16.434252000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 5.359384500000,28.781305000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 17.706438000000,41.128359000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 26.811783336064,37.120515321723"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 26.811783336064,37.120515321723 A 12.347054000000 12.347054000000 0.000000000000 0 0 30.053492000000,28.781305000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 27.251230395016,20.948893816326 A 8.683698700000 8.683698700000 0.000000000000 0 0 21.505442000000,29.120509958794 A 8.683698700000 8.683698700000 0.000000000000 0 0 26.811783335962,37.120515321746"/><path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 26.811783335962,37.120515321746 A 8.683698700000 8.683698700000 0.000000000000 0 0 30.189140000000,37.804209000000 A 8.683698700000 8.683698700000 0.000000000000 0 0 38.872839000000,29.120510000000 A 8.683698700000 8.683698700000 0.000000000000 0 0 30.189140000000,20.436811000000 A 8.683698700000 8.683698700000 0.000000000000 0 0 27.251230394932,20.948893816238 A 12.347054000000 12.347054000000 0.000000000000 0 1 30.053492000000,28.781305000000 A 12.347054000000 12.347054000000 0.000000000000 0 1 26.811783336064,37.120515321723"/><path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 26.811783335962,37.120515321746 A 8.683698700000 8.683698700000 0.000000000000 0 1 21.505442000000,29.120510000000 A 8.683698700000 8.683698700000 0.000000000000 0 1 27.251230394932,20.948893816238 A 12.347054000000 12.347054000000 0.000000000000 0 0 17.706438000000,16.434252000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 5.359384500000,28.781305000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 17.706438000000,41.128359000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 26.811783336064,37.120515321723"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 26.811783336064,37.120515321723 A 12.347054000000 12.347054000000 0.000000000000 0 0 30.053492000000,28.781305000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 27.251230395016,20.948893816326 A 8.683698700000 8.683698700000 0.000000000000 0 0 21.505442000000,29.120509958794 A 8.683698700000 8.683698700000 0.000000000000 0 0 26.811783335962,37.120515321746"/>
</svg>

Before

Width:  |  Height:  |  Size: 720 B

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48"
height="48"
viewBox="0 0 48 48"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg">
<path id="a"
style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round"
d="M 30.053492,28.781305 A 12.347054,12.347054 0 0 1 17.706438,41.128359 12.347054,12.347054 0 0 1 5.3593845,28.781305 12.347054,12.347054 0 0 1 17.706438,16.434252 12.347054,12.347054 0 0 1 30.053492,28.781305 Z"/>
<path id="b"
style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round"
d="M 38.872839,29.12051 A 8.6836987,8.6836987 0 0 1 30.18914,37.804209 8.6836987,8.6836987 0 0 1 21.505442,29.12051 8.6836987,8.6836987 0 0 1 30.18914,20.436811 8.6836987,8.6836987 0 0 1 38.872839,29.12051 Z q 10,-10 0,-20"/>
</svg>

Before

Width:  |  Height:  |  Size: 970 B

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 26.811783335962,37.120515321746 A 8.683698700000 8.683698700000 0.000000000000 0 0 30.189140000000,37.804209000000 A 8.683698700000 8.683698700000 0.000000000000 0 0 38.872839000000,29.120510000000 A 8.683698700000 8.683698700000 0.000000000000 0 0 30.189140000000,20.436811000000 A 8.683698700000 8.683698700000 0.000000000000 0 0 27.251230394932,20.948893816238 A 12.347054000000 12.347054000000 0.000000000000 0 0 17.706438000000,16.434252000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 5.359384500000,28.781305000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 17.706438000000,41.128359000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 26.811783336064,37.120515321723"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 26.811783335962,37.120515321746 A 8.683698700000 8.683698700000 0.000000000000 0 1 21.505442000000,29.120510000000 A 8.683698700000 8.683698700000 0.000000000000 0 1 27.251230394932,20.948893816238 A 12.347054000000 12.347054000000 0.000000000000 0 0 17.706438000000,16.434252000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 5.359384500000,28.781305000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 17.706438000000,41.128359000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 26.811783336064,37.120515321723"/>
</svg>

Before

Width:  |  Height:  |  Size: 889 B

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 30.053492000000,28.781305000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 27.251230395016,20.948893816326 A 8.683698700000 8.683698700000 0.000000000000 0 0 21.505442000000,29.120509958794 A 8.683698700000 8.683698700000 0.000000000000 0 0 26.811783335962,37.120515321746 A 12.347054000000 12.347054000000 0.000000000000 0 0 30.053492000000,28.781305000000"/><path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 26.811783335962,37.120515321746 A 8.683698700000 8.683698700000 0.000000000000 0 1 21.505442000000,29.120510000000 A 8.683698700000 8.683698700000 0.000000000000 0 1 27.251230394932,20.948893816238 A 12.347054000000 12.347054000000 0.000000000000 0 0 17.706438000000,16.434252000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 5.359384500000,28.781305000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 17.706438000000,41.128359000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 26.811783336064,37.120515321723"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 30.053492000000,28.781305000000 A 12.347054000000 12.347054000000 0.000000000000 0 1 26.811783336064,37.120515321723 A 8.683698700000 8.683698700000 0.000000000000 0 0 30.189140000000,37.804209000000 A 8.683698700000 8.683698700000 0.000000000000 0 0 38.872839000000,29.120510000000 A 8.683698700000 8.683698700000 0.000000000000 0 0 34.638239102979,21.663154743066 A 8.683698700000 8.683698700000 0.000000000000 0 0 30.189140000000,20.436811000000 A 8.683698700000 8.683698700000 0.000000000000 0 0 27.251230394932,20.948893816238 A 12.347054000000 12.347054000000 0.000000000000 0 1 30.053492000000,28.781305000000 M 26.811783335962,37.120515321746 A 8.683698700000 8.683698700000 0.000000000000 0 1 21.505442000000,29.120510000000 A 8.683698700000 8.683698700000 0.000000000000 0 1 27.251230394932,20.948893816238 A 12.347054000000 12.347054000000 0.000000000000 0 0 17.706438000000,16.434252000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 5.359384500000,28.781305000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 17.706438000000,41.128359000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 26.811783336064,37.120515321723"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 30.053492000000,28.781305000000 A 12.347054000000 12.347054000000 0.000000000000 0 1 26.811783336064,37.120515321723 A 8.683698700000 8.683698700000 0.000000000000 0 0 30.189140000000,37.804209000000 A 8.683698700000 8.683698700000 0.000000000000 0 0 38.872839000000,29.120510000000 A 8.683698700000 8.683698700000 0.000000000000 0 0 34.638239102979,21.663154743066 A 8.683698700000 8.683698700000 0.000000000000 0 0 30.189140000000,20.436811000000 A 8.683698700000 8.683698700000 0.000000000000 0 0 27.251230394932,20.948893816238 A 12.347054000000 12.347054000000 0.000000000000 0 1 30.053492000000,28.781305000000"/><path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 30.053492000000,28.781305000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 27.251230395016,20.948893816326 A 8.683698700000 8.683698700000 0.000000000000 0 0 21.505442000000,29.120509958794 A 8.683698700000 8.683698700000 0.000000000000 0 0 26.811783335962,37.120515321746 A 12.347054000000 12.347054000000 0.000000000000 0 0 30.053492000000,28.781305000000"/><path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 26.811783335962,37.120515321746 A 8.683698700000 8.683698700000 0.000000000000 0 1 21.505442000000,29.120510000000 A 8.683698700000 8.683698700000 0.000000000000 0 1 27.251230394932,20.948893816238 A 12.347054000000 12.347054000000 0.000000000000 0 0 17.706438000000,16.434252000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 5.359384500000,28.781305000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 17.706438000000,41.128359000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 26.811783336064,37.120515321723"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 30.053492000000,28.781305000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 27.251230395016,20.948893816326 A 8.683698700000 8.683698700000 0.000000000000 0 0 21.505442000000,29.120509958794 A 8.683698700000 8.683698700000 0.000000000000 0 0 26.811783335962,37.120515321746 A 12.347054000000 12.347054000000 0.000000000000 0 0 30.053492000000,28.781305000000"/>
</svg>

Before

Width:  |  Height:  |  Size: 720 B

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48"
height="48"
viewBox="0 0 48 48"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg">
<path id="a"
style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round"
d="M 30.053492,28.781305 A 12.347054,12.347054 0 0 1 17.706438,41.128359 12.347054,12.347054 0 0 1 5.3593845,28.781305 12.347054,12.347054 0 0 1 17.706438,16.434252 12.347054,12.347054 0 0 1 30.053492,28.781305 Z q 10,-10 0,-20"/>
<path id="b"
style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round"
d="M 38.872839,29.12051 A 8.6836987,8.6836987 0 0 1 30.18914,37.804209 8.6836987,8.6836987 0 0 1 21.505442,29.12051 8.6836987,8.6836987 0 0 1 30.18914,20.436811 8.6836987,8.6836987 0 0 1 38.872839,29.12051 Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 970 B

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 26.811783335962,37.120515321746 A 8.683698700000 8.683698700000 0.000000000000 0 0 30.189140000000,37.804209000000 A 8.683698700000 8.683698700000 0.000000000000 0 0 38.872839000000,29.120510000000 A 8.683698700000 8.683698700000 0.000000000000 0 0 34.638239102979,21.663154743066 A 8.683698700000 8.683698700000 0.000000000000 0 0 30.189140000000,20.436811000000 A 8.683698700000 8.683698700000 0.000000000000 0 0 27.251230394932,20.948893816238 A 12.347054000000 12.347054000000 0.000000000000 0 0 17.706438000000,16.434252000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 5.359384500000,28.781305000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 17.706438000000,41.128359000000 A 12.347054000000 12.347054000000 0.000000000000 0 0 26.811783336064,37.120515321723"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d=""/>
</svg>

Before

Width:  |  Height:  |  Size: 351 B

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
</svg>

Before

Width:  |  Height:  |  Size: 239 B

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d=""/>
</svg>

Before

Width:  |  Height:  |  Size: 351 B

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
</svg>

Before

Width:  |  Height:  |  Size: 239 B

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d=""/>
</svg>

Before

Width:  |  Height:  |  Size: 351 B

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48"
height="48"
viewBox="0 0 48 48"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round"
d="M 7.5980087,6.9364914 C 18.588406,7.9541084 22.115946,15.280819 21.098329,20.776017 c -1.017617,5.495199 -1.899419,10.583615 2.171049,14.179063 4.070468,3.595448 2.103141,10.040025 -14.2466391,8.886924"
id="a"/>
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round"
d="m 32.835045,8.3612215 c 0,0 -17.028192,1.6281211 -17.0961,10.1082635 -0.06791,8.480142 10.379893,5.2239 10.990397,8.276751 0.610504,3.052851 -9.362276,7.530433 -8.887255,10.515377 0.47502,2.984943 6.580391,13.364505 20.555732,3.52754"
id="b"/>
</svg>

Before

Width:  |  Height:  |  Size: 976 B

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d=""/>
</svg>

Before

Width:  |  Height:  |  Size: 351 B

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 24.477098604512,9.669417777521 C 14.819439929541,11.947535742517 12.676649436509,16.038615428837 14.333890728881,19.259441941784 A 14.414713000000 14.414713000000 0.000000000000 0 1 24.000000000000,15.538198000000 A 14.414713000000 14.414713000000 0.000000000000 0 1 33.122169882593,18.791819319278 C 34.069717470896,15.767492825406 31.940527089963,12.079149310707 24.477098604560,9.669417777522"/>
</svg>

Before

Width:  |  Height:  |  Size: 742 B

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 24.477098604512,9.669417777521 C 14.819439929541,11.947535742517 12.676649436509,16.038615428837 14.333890728881,19.259441941784 A 14.414713000000 14.414713000000 0.000000000000 0 1 24.000000000000,15.538198000000 A 14.414713000000 14.414713000000 0.000000000000 0 1 33.122169882593,18.791819319278 C 34.069717470896,15.767492825406 31.940527089963,12.079149310707 24.477098604560,9.669417777522"/><path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 33.122169882593,18.791819319278 A 14.414713000000 14.414713000000 0.000000000000 0 0 24.000000000000,15.538198000000 A 14.414713000000 14.414713000000 0.000000000000 0 0 14.333890728979,19.259441941746 C 15.689970608910,21.894965236658 19.590455736640,23.947789000000 24.000000000000,23.947789000000 C 28.530465205736,23.947789000000 32.220212847621,21.670632574670 33.122169882598,18.791819319326"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 24.477098604512,9.669417777521 C 14.819439929541,11.947535742517 12.676649436509,16.038615428837 14.333890728881,19.259441941784 A 14.414713000000 14.414713000000 0.000000000000 0 1 24.000000000000,15.538198000000 A 14.414713000000 14.414713000000 0.000000000000 0 1 33.122169882593,18.791819319278 C 34.069717470896,15.767492825406 31.940527089963,12.079149310707 24.477098604560,9.669417777522 M 33.122169882598,18.791819319326 C 32.220212847621,21.670632574670 28.530465205736,23.947789000000 24.000000000000,23.947789000000 C 19.590455736640,23.947789000000 15.689970608910,21.894965236658 14.333890728881,19.259441941784 A 14.414713000000 14.414713000000 0.000000000000 0 0 9.585287100000,29.952911000000 A 14.414713000000 14.414713000000 0.000000000000 0 0 24.000000000000,44.367624000000 A 14.414713000000 14.414713000000 0.000000000000 0 0 38.414713000000,29.952911000000 A 14.414713000000 14.414713000000 0.000000000000 0 0 33.122169882593,18.791819319278"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 24.477098604512,9.669417777521 C 14.819439929541,11.947535742517 12.676649436509,16.038615428837 14.333890728881,19.259441941784 A 14.414713000000 14.414713000000 0.000000000000 0 1 24.000000000000,15.538198000000 A 14.414713000000 14.414713000000 0.000000000000 0 1 33.122169882593,18.791819319278 C 34.069717470896,15.767492825406 31.940527089963,12.079149310707 24.477098604560,9.669417777522"/><path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 33.122169882598,18.791819319326 C 32.220212847621,21.670632574670 28.530465205736,23.947789000000 24.000000000000,23.947789000000 C 19.590455736640,23.947789000000 15.689970608910,21.894965236658 14.333890728881,19.259441941784 A 14.414713000000 14.414713000000 0.000000000000 0 0 9.585287100000,29.952911000000 A 14.414713000000 14.414713000000 0.000000000000 0 0 24.000000000000,44.367624000000 A 14.414713000000 14.414713000000 0.000000000000 0 0 38.414713000000,29.952911000000 A 14.414713000000 14.414713000000 0.000000000000 0 0 33.122169882593,18.791819319278"/><path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 33.122169882593,18.791819319278 A 14.414713000000 14.414713000000 0.000000000000 0 0 24.000000000000,15.538198000000 A 14.414713000000 14.414713000000 0.000000000000 0 0 14.333890728979,19.259441941746 C 15.689970608910,21.894965236658 19.590455736640,23.947789000000 24.000000000000,23.947789000000 C 28.530465205736,23.947789000000 32.220212847621,21.670632574670 33.122169882598,18.791819319326"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 33.122169882593,18.791819319278 A 14.414713000000 14.414713000000 0.000000000000 0 0 24.000000000000,15.538198000000 A 14.414713000000 14.414713000000 0.000000000000 0 0 14.333890728979,19.259441941746 C 15.689970608910,21.894965236658 19.590455736640,23.947789000000 24.000000000000,23.947789000000 C 28.530465205736,23.947789000000 32.220212847621,21.670632574670 33.122169882598,18.791819319326"/>
</svg>

Before

Width:  |  Height:  |  Size: 744 B

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48"
height="48"
viewBox="0 0 48 48"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
d="M 3.2052288,7.1229882 C 42.417605,7.1229882 36.314629,23.947789 24,23.947789 11.685371,23.947789 3.3410435,7.9372144 43.299408,7.9372144"
id="a"/>
<path id="b"
style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
d="M 38.414713,29.952911 A 14.414713,14.414713 0 0 1 24,44.367624 14.414713,14.414713 0 0 1 9.5852871,29.952911 14.414713,14.414713 0 0 1 24,15.538198 14.414713,14.414713 0 0 1 38.414713,29.952911 Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 861 B

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 24.477098604512,9.669417777521 C 14.819439929541,11.947535742517 12.676649436509,16.038615428837 14.333890728881,19.259441941784 A 14.414713000000 14.414713000000 0.000000000000 0 0 9.585287100000,29.952911000000 A 14.414713000000 14.414713000000 0.000000000000 0 0 24.000000000000,44.367624000000 A 14.414713000000 14.414713000000 0.000000000000 0 0 38.414713000000,29.952911000000 A 14.414713000000 14.414713000000 0.000000000000 0 0 33.122169882593,18.791819319278 C 34.069717470896,15.767492825406 31.940527089963,12.079149310707 24.477098604560,9.669417777522"/>
</svg>

Before

Width:  |  Height:  |  Size: 911 B

View File

@ -1,5 +0,0 @@
for dir in */; do
for fn in difference division exclusion fracture intersection union; do
cp "${dir}test-results/$fn-ours.svg" "$dir$fn.svg"
done
done

View File

@ -1,10 +0,0 @@
INKSCAPE_CMD=inkscape
OPS=(union difference intersection exclusion division fracture)
for dir in */; do
for op in "${OPS[@]}"; do
if [ ! -e "$dir/$op.svg" ]; then
$INKSCAPE_CMD --actions="select-all; path-$op; export-filename:$dir/$op.svg; export-plain-svg; export-do; file-close" "$dir/original.svg"
fi
done
done

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<defs id="defs1"/>
<g id="layer1">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 47.000000000000,24.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 24.000000000000,1.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 1.000000000000,24.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 24.000000000000,47.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 47.000000000000,24.000000000000 M 37.909023000000,24.000000000000 A 13.909023000000 13.909023000000 0.000000000000 0 1 24.000000000000,37.909023000000 A 13.909023000000 13.909023000000 0.000000000000 0 1 10.090978000000,24.000000000000 A 13.909023000000 13.909023000000 0.000000000000 0 1 24.000000000000,10.090978000000 A 13.909023000000 13.909023000000 0.000000000000 0 1 37.909023000000,24.000000000000"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<defs id="defs1"/>
<g id="layer1">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 47.000000000000,24.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 24.000000000000,1.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 1.000000000000,24.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 24.000000000000,47.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 47.000000000000,24.000000000000 M 37.909023000000,24.000000000000 A 13.909023000000 13.909023000000 0.000000000000 0 1 24.000000000000,37.909023000000 A 13.909023000000 13.909023000000 0.000000000000 0 1 10.090978000000,24.000000000000 A 13.909023000000 13.909023000000 0.000000000000 0 1 24.000000000000,10.090978000000 A 13.909023000000 13.909023000000 0.000000000000 0 1 37.909023000000,24.000000000000"/><path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 37.909023000000,24.000000000000 A 13.909023000000 13.909023000000 0.000000000000 0 0 24.000000000000,10.090978000000 A 13.909023000000 13.909023000000 0.000000000000 0 0 10.090978000000,24.000000000000 A 13.909023000000 13.909023000000 0.000000000000 0 0 24.000000000000,37.909023000000 A 13.909023000000 13.909023000000 0.000000000000 0 0 37.909023000000,24.000000000000"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<defs id="defs1"/>
<g id="layer1">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 47.000000000000,24.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 24.000000000000,1.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 1.000000000000,24.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 24.000000000000,47.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 47.000000000000,24.000000000000 M 37.909023000000,24.000000000000 A 13.909023000000 13.909023000000 0.000000000000 0 1 24.000000000000,37.909023000000 A 13.909023000000 13.909023000000 0.000000000000 0 1 10.090978000000,24.000000000000 A 13.909023000000 13.909023000000 0.000000000000 0 1 24.000000000000,10.090978000000 A 13.909023000000 13.909023000000 0.000000000000 0 1 37.909023000000,24.000000000000"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<defs id="defs1"/>
<g id="layer1">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 47.000000000000,24.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 24.000000000000,1.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 1.000000000000,24.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 24.000000000000,47.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 47.000000000000,24.000000000000 M 37.909023000000,24.000000000000 A 13.909023000000 13.909023000000 0.000000000000 0 1 24.000000000000,37.909023000000 A 13.909023000000 13.909023000000 0.000000000000 0 1 10.090978000000,24.000000000000 A 13.909023000000 13.909023000000 0.000000000000 0 1 24.000000000000,10.090978000000 A 13.909023000000 13.909023000000 0.000000000000 0 1 37.909023000000,24.000000000000"/><path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 37.909023000000,24.000000000000 A 13.909023000000 13.909023000000 0.000000000000 0 0 24.000000000000,10.090978000000 A 13.909023000000 13.909023000000 0.000000000000 0 0 10.090978000000,24.000000000000 A 13.909023000000 13.909023000000 0.000000000000 0 0 24.000000000000,37.909023000000 A 13.909023000000 13.909023000000 0.000000000000 0 0 37.909023000000,24.000000000000"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<defs id="defs1"/>
<g id="layer1">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 37.909023000000,24.000000000000 A 13.909023000000 13.909023000000 0.000000000000 0 0 24.000000000000,10.090978000000 A 13.909023000000 13.909023000000 0.000000000000 0 0 10.090978000000,24.000000000000 A 13.909023000000 13.909023000000 0.000000000000 0 0 24.000000000000,37.909023000000 A 13.909023000000 13.909023000000 0.000000000000 0 0 37.909023000000,24.000000000000"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 778 B

View File

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="48"
height="48"
viewBox="0 0 48 48"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
>
<defs
id="defs1"/>
<g
id="layer1">
<path
id="a"
style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
d="M 47,24 A 23,23 0 0 1 24,47 23,23 0 0 1 1,24 23,23 0 0 1 24,1 23,23 0 0 1 47,24 Z"/>
<path
id="b"
style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
d="M 37.909023,24 A 13.909023,13.909023 0 0 1 24,37.909023 13.909023,13.909023 0 0 1 10.090978,24 13.909023,13.909023 0 0 1 24,10.090978 13.909023,13.909023 0 0 1 37.909023,24 Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 949 B

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<defs id="defs1"/>
<g id="layer1">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 47.000000000000,24.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 24.000000000000,1.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 1.000000000000,24.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 24.000000000000,47.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 47.000000000000,24.000000000000"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 776 B

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<defs id="defs1"/>
<g id="layer1">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" d="M 0.999999940000,31.334457000000 L 0.999999940000,46.999996000000 L 47.000000000000,46.999996000000 L 47.000000000000,32.253367000000 C -79.025816000000,-5.580332600000 122.611950000000,71.818590000000 0.999999940000,31.334457000000 M 25.797222000000,29.087180000000 C 25.797222000000,30.379886000000 24.749276000000,31.427832000000 23.456570000000,31.427832000000 C 22.163863000000,31.427832000000 21.115918000000,30.379886000000 21.115918000000,29.087180000000 C 21.115918000000,27.794473000000 22.163863000000,26.746528000000 23.456570000000,26.746528000000 C 24.749276000000,26.746528000000 25.797222000000,27.794473000000 25.797222000000,29.087180000000"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<defs id="defs1"/>
<g id="layer1">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" d="M 0.999999940000,31.334457000000 L 0.999999940000,46.999996000000 L 47.000000000000,46.999996000000 L 47.000000000000,32.253367000000 C -79.025816000000,-5.580332600000 122.611950000000,71.818590000000 0.999999940000,31.334457000000 M 25.797222000000,29.087180000000 C 25.797222000000,30.379886000000 24.749276000000,31.427832000000 23.456570000000,31.427832000000 C 22.163863000000,31.427832000000 21.115918000000,30.379886000000 21.115918000000,29.087180000000 C 21.115918000000,27.794473000000 22.163863000000,26.746528000000 23.456570000000,26.746528000000 C 24.749276000000,26.746528000000 25.797222000000,27.794473000000 25.797222000000,29.087180000000"/><path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" d="M 25.797222000000,29.087180000000 C 25.797222000000,27.794473000000 24.749276000000,26.746528000000 23.456570000000,26.746528000000 C 22.163863000000,26.746528000000 21.115918000000,27.794473000000 21.115918000000,29.087180000000 C 21.115918000000,30.379886000000 22.163863000000,31.427832000000 23.456570000000,31.427832000000 C 24.749276000000,31.427832000000 25.797222000000,30.379886000000 25.797222000000,29.087180000000"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<defs id="defs1"/>
<g id="layer1">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" d="M 7.585107300000,28.332212000000 C 7.585107400000,27.039505000000 6.537161700000,25.991559000000 5.244455200000,25.991560000000 C 3.951748900000,25.991560000000 2.903803400000,27.039505000000 2.903803500000,28.332212000000 C 2.903803400000,29.624918000000 3.951748900000,30.672863000000 5.244455200000,30.672864000000 C 6.537161700000,30.672864000000 7.585107400000,29.624918000000 7.585107300000,28.332212000000 M 0.999999940000,31.334457000000 L 0.999999940000,46.999996000000 L 47.000000000000,46.999996000000 L 47.000000000000,32.253367000000 C -79.025816000000,-5.580332600000 122.611950000000,71.818590000000 0.999999940000,31.334457000000 M 25.797222000000,29.087180000000 C 25.797222000000,30.379886000000 24.749276000000,31.427832000000 23.456570000000,31.427832000000 C 22.163863000000,31.427832000000 21.115918000000,30.379886000000 21.115918000000,29.087180000000 C 21.115918000000,27.794473000000 22.163863000000,26.746528000000 23.456570000000,26.746528000000 C 24.749276000000,26.746528000000 25.797222000000,27.794473000000 25.797222000000,29.087180000000"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<defs id="defs1"/>
<g id="layer1">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" d="M 7.585107300000,28.332212000000 C 7.585107400000,27.039505000000 6.537161700000,25.991559000000 5.244455200000,25.991560000000 C 3.951748900000,25.991560000000 2.903803400000,27.039505000000 2.903803500000,28.332212000000 C 2.903803400000,29.624918000000 3.951748900000,30.672863000000 5.244455200000,30.672864000000 C 6.537161700000,30.672864000000 7.585107400000,29.624918000000 7.585107300000,28.332212000000"/><path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" d="M 0.999999940000,31.334457000000 L 0.999999940000,46.999996000000 L 47.000000000000,46.999996000000 L 47.000000000000,32.253367000000 C -79.025816000000,-5.580332600000 122.611950000000,71.818590000000 0.999999940000,31.334457000000 M 25.797222000000,29.087180000000 C 25.797222000000,30.379886000000 24.749276000000,31.427832000000 23.456570000000,31.427832000000 C 22.163863000000,31.427832000000 21.115918000000,30.379886000000 21.115918000000,29.087180000000 C 21.115918000000,27.794473000000 22.163863000000,26.746528000000 23.456570000000,26.746528000000 C 24.749276000000,26.746528000000 25.797222000000,27.794473000000 25.797222000000,29.087180000000"/><path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" d="M 25.797222000000,29.087180000000 C 25.797222000000,27.794473000000 24.749276000000,26.746528000000 23.456570000000,26.746528000000 C 22.163863000000,26.746528000000 21.115918000000,27.794473000000 21.115918000000,29.087180000000 C 21.115918000000,30.379886000000 22.163863000000,31.427832000000 23.456570000000,31.427832000000 C 24.749276000000,31.427832000000 25.797222000000,30.379886000000 25.797222000000,29.087180000000"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<defs id="defs1"/>
<g id="layer1">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" d="M 25.797222000000,29.087180000000 C 25.797222000000,27.794473000000 24.749276000000,26.746528000000 23.456570000000,26.746528000000 C 22.163863000000,26.746528000000 21.115918000000,27.794473000000 21.115918000000,29.087180000000 C 21.115918000000,30.379886000000 22.163863000000,31.427832000000 23.456570000000,31.427832000000 C 24.749276000000,31.427832000000 25.797222000000,30.379886000000 25.797222000000,29.087180000000"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 877 B

View File

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="48"
height="48"
viewBox="0 0 48 48"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<g
id="layer1">
<path
id="a"
style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
d="M 0.99999994,31.334457 C 122.61195,71.81859 -79.025816,-5.5803326 47,32.253367 V 46.999996 H 0.99999994 Z" />
<path
id="b"
style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
d="m 25.797222,29.08718 c 0,1.292706 -1.047946,2.340652 -2.340652,2.340652 -1.292707,0 -2.340652,-1.047946 -2.340652,-2.340652 0,-1.292707 1.047945,-2.340652 2.340652,-2.340652 1.292706,0 2.340652,1.047945 2.340652,2.340652 z M 7.5851073,28.332212 c 1e-7,1.292706 -1.0479456,2.340652 -2.3406521,2.340652 -1.2927063,-1e-6 -2.3406518,-1.047946 -2.3406517,-2.340652 -10e-8,-1.292707 1.0479454,-2.340652 2.3406517,-2.340652 1.2927065,-1e-6 2.3406522,1.047945 2.3406521,2.340652 z" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<defs id="defs1"/>
<g id="layer1">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" d="M 7.585107300000,28.332212000000 C 7.585107400000,27.039505000000 6.537161700000,25.991559000000 5.244455200000,25.991560000000 C 3.951748900000,25.991560000000 2.903803400000,27.039505000000 2.903803500000,28.332212000000 C 2.903803400000,29.624918000000 3.951748900000,30.672863000000 5.244455200000,30.672864000000 C 6.537161700000,30.672864000000 7.585107400000,29.624918000000 7.585107300000,28.332212000000 M 0.999999940000,31.334457000000 L 0.999999940000,46.999996000000 L 47.000000000000,46.999996000000 L 47.000000000000,32.253367000000 C -79.025816000000,-5.580332600000 122.611950000000,71.818590000000 0.999999940000,31.334457000000"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 47.000000000000,24.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 24.000000000000,1.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 1.000000000000,24.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 24.000000000000,47.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 47.000000000000,24.000000000000 M 21.829116999931,6.631339939284 A 17.504802000000 17.504802000000 0.000000000000 0 1 24.000000000000,6.496093800000 A 17.504802000000 17.504802000000 0.000000000000 0 1 26.170883000441,6.631339964327 A 17.504802000000 17.504802000000 0.000000000000 0 1 41.503906000000,24.000000000037 A 17.504802000000 17.504802000000 0.000000000000 0 1 24.000000000000,41.503906000000 A 17.504802000000 17.504802000000 0.000000000000 0 1 6.496093800000,24.000000000000 A 17.504802000000 17.504802000000 0.000000000000 0 1 21.829116999931,6.631339939284 M 21.829117000123,12.394525741323 A 11.805881000000 11.805881000000 0.000000000000 0 0 12.193359000000,24.000000000011 A 11.805881000000 11.805881000000 0.000000000000 0 0 24.000000000000,35.806641000000 A 11.805881000000 11.805881000000 0.000000000000 0 0 35.806641000000,24.000000000000 A 11.805881000000 11.805881000000 0.000000000000 0 0 26.170883000024,12.394525741351 A 11.805881000000 11.805881000000 0.000000000000 0 0 24.000000000000,12.193359000000 A 11.805881000000 11.805881000000 0.000000000000 0 0 21.829117000123,12.394525741323"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 47.000000000000,24.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 24.000000000000,1.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 1.000000000000,24.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 24.000000000000,47.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 47.000000000000,24.000000000000 M 26.170883000441,6.631339964327 A 17.504802000000 17.504802000000 0.000000000000 0 1 41.503906000000,24.000000000037 A 17.504802000000 17.504802000000 0.000000000000 0 1 24.000000000000,41.503906000000 A 17.504802000000 17.504802000000 0.000000000000 0 1 6.496093800000,24.000000000000 A 17.504802000000 17.504802000000 0.000000000000 0 1 21.829116999931,6.631339939284 L 21.829117000000,3.544434500000 L 26.170883000000,3.544434500000 L 26.170883000000,6.631339964278"/><path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 26.170883000000,6.631339964278 L 26.170883000000,3.544434500000 L 21.829117000000,3.544434500000 L 21.829117000000,6.631339939282 A 17.504802000000 17.504802000000 0.000000000000 0 1 24.000000000000,6.496093800000 A 17.504802000000 17.504802000000 0.000000000000 0 1 26.170883000441,6.631339964327"/><path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 26.170883000000,6.631339964278 L 26.170883000000,12.394525741375 A 11.805881000000 11.805881000000 0.000000000000 0 1 35.806641000000,24.000000000000 A 11.805881000000 11.805881000000 0.000000000000 0 1 24.000000000000,35.806641000000 A 11.805881000000 11.805881000000 0.000000000000 0 1 12.193359000000,24.000000000000 A 11.805881000000 11.805881000000 0.000000000000 0 1 21.829117000123,12.394525741323 L 21.829117000000,6.631339939282 A 17.504802000000 17.504802000000 0.000000000000 0 0 6.496093800000,24.000000000000 A 17.504802000000 17.504802000000 0.000000000000 0 0 24.000000000000,41.503906000000 A 17.504802000000 17.504802000000 0.000000000000 0 0 41.503906000000,24.000000000000 A 17.504802000000 17.504802000000 0.000000000000 0 0 26.170883000441,6.631339964327"/><path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 26.170883000441,6.631339964327 A 17.504802000000 17.504802000000 0.000000000000 0 0 24.000000000037,6.496093800000 A 17.504802000000 17.504802000000 0.000000000000 0 0 21.829116999931,6.631339939284 L 21.829117000000,12.394525741375 A 11.805881000000 11.805881000000 0.000000000000 0 1 23.999999999989,12.193359000000 A 11.805881000000 11.805881000000 0.000000000000 0 1 26.170883000024,12.394525741351 L 26.170883000000,6.631339964278"/><path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 26.170883000000,12.394525741375 L 26.170883000000,16.502158000000 L 21.829117000000,16.502158000000 L 21.829117000000,12.394525741375 A 11.805881000000 11.805881000000 0.000000000000 0 0 12.193359000000,24.000000000011 A 11.805881000000 11.805881000000 0.000000000000 0 0 24.000000000000,35.806641000000 A 11.805881000000 11.805881000000 0.000000000000 0 0 35.806641000000,24.000000000000 A 11.805881000000 11.805881000000 0.000000000000 0 0 26.170883000024,12.394525741351"/><path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 26.170883000024,12.394525741351 A 11.805881000000 11.805881000000 0.000000000000 0 0 24.000000000000,12.193359000000 A 11.805881000000 11.805881000000 0.000000000000 0 0 21.829117000123,12.394525741323 L 21.829117000000,16.502158000000 L 26.170883000000,16.502158000000 L 26.170883000000,12.394525741375"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 47.000000000000,24.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 24.000000000000,1.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 1.000000000000,24.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 24.000000000000,47.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 47.000000000000,24.000000000000 M 21.829116999931,6.631339939284 A 17.504802000000 17.504802000000 0.000000000000 0 1 24.000000000000,6.496093800000 A 17.504802000000 17.504802000000 0.000000000000 0 1 26.170883000441,6.631339964327 A 17.504802000000 17.504802000000 0.000000000000 0 1 41.503906000000,24.000000000037 A 17.504802000000 17.504802000000 0.000000000000 0 1 24.000000000000,41.503906000000 A 17.504802000000 17.504802000000 0.000000000000 0 1 6.496093800000,24.000000000000 A 17.504802000000 17.504802000000 0.000000000000 0 1 21.829116999931,6.631339939284 M 21.829117000123,12.394525741323 A 11.805881000000 11.805881000000 0.000000000000 0 0 12.193359000000,24.000000000011 A 11.805881000000 11.805881000000 0.000000000000 0 0 24.000000000000,35.806641000000 A 11.805881000000 11.805881000000 0.000000000000 0 0 35.806641000000,24.000000000000 A 11.805881000000 11.805881000000 0.000000000000 0 0 26.170883000024,12.394525741351 A 11.805881000000 11.805881000000 0.000000000000 0 0 24.000000000000,12.193359000000 A 11.805881000000 11.805881000000 0.000000000000 0 0 21.829117000123,12.394525741323"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 47.000000000000,24.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 24.000000000000,1.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 1.000000000000,24.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 24.000000000000,47.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 47.000000000000,24.000000000000 M 26.170883000441,6.631339964327 A 17.504802000000 17.504802000000 0.000000000000 0 1 41.503906000000,24.000000000037 A 17.504802000000 17.504802000000 0.000000000000 0 1 24.000000000000,41.503906000000 A 17.504802000000 17.504802000000 0.000000000000 0 1 6.496093800000,24.000000000000 A 17.504802000000 17.504802000000 0.000000000000 0 1 21.829116999931,6.631339939284 L 21.829117000000,3.544434500000 L 26.170883000000,3.544434500000 L 26.170883000000,6.631339964278"/><path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 26.170883000000,6.631339964278 L 26.170883000000,3.544434500000 L 21.829117000000,3.544434500000 L 21.829117000000,6.631339939282 A 17.504802000000 17.504802000000 0.000000000000 0 1 24.000000000000,6.496093800000 A 17.504802000000 17.504802000000 0.000000000000 0 1 26.170883000441,6.631339964327"/><path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 26.170883000000,6.631339964278 L 26.170883000000,12.394525741375 A 11.805881000000 11.805881000000 0.000000000000 0 1 35.806641000000,24.000000000000 A 11.805881000000 11.805881000000 0.000000000000 0 1 24.000000000000,35.806641000000 A 11.805881000000 11.805881000000 0.000000000000 0 1 12.193359000000,24.000000000000 A 11.805881000000 11.805881000000 0.000000000000 0 1 21.829117000123,12.394525741323 L 21.829117000000,6.631339939282 A 17.504802000000 17.504802000000 0.000000000000 0 0 6.496093800000,24.000000000000 A 17.504802000000 17.504802000000 0.000000000000 0 0 24.000000000000,41.503906000000 A 17.504802000000 17.504802000000 0.000000000000 0 0 41.503906000000,24.000000000000 A 17.504802000000 17.504802000000 0.000000000000 0 0 26.170883000441,6.631339964327"/><path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 26.170883000441,6.631339964327 A 17.504802000000 17.504802000000 0.000000000000 0 0 24.000000000037,6.496093800000 A 17.504802000000 17.504802000000 0.000000000000 0 0 21.829116999931,6.631339939284 L 21.829117000000,12.394525741375 A 11.805881000000 11.805881000000 0.000000000000 0 1 23.999999999989,12.193359000000 A 11.805881000000 11.805881000000 0.000000000000 0 1 26.170883000024,12.394525741351 L 26.170883000000,6.631339964278"/><path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 26.170883000000,12.394525741375 L 26.170883000000,16.502158000000 L 21.829117000000,16.502158000000 L 21.829117000000,12.394525741375 A 11.805881000000 11.805881000000 0.000000000000 0 0 12.193359000000,24.000000000011 A 11.805881000000 11.805881000000 0.000000000000 0 0 24.000000000000,35.806641000000 A 11.805881000000 11.805881000000 0.000000000000 0 0 35.806641000000,24.000000000000 A 11.805881000000 11.805881000000 0.000000000000 0 0 26.170883000024,12.394525741351"/><path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 26.170883000024,12.394525741351 A 11.805881000000 11.805881000000 0.000000000000 0 0 24.000000000000,12.193359000000 A 11.805881000000 11.805881000000 0.000000000000 0 0 21.829117000123,12.394525741323 L 21.829117000000,16.502158000000 L 26.170883000000,16.502158000000 L 26.170883000000,12.394525741375"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 26.170883000024,12.394525741351 A 11.805881000000 11.805881000000 0.000000000000 0 1 35.806641000000,24.000000000000 A 11.805881000000 11.805881000000 0.000000000000 0 1 24.000000000000,35.806641000000 A 11.805881000000 11.805881000000 0.000000000000 0 1 12.193359000000,24.000000000000 A 11.805881000000 11.805881000000 0.000000000000 0 1 21.829117000123,12.394525741323 A 11.805881000000 11.805881000000 0.000000000000 0 1 23.999999999989,12.193359000000 A 11.805881000000 11.805881000000 0.000000000000 0 1 26.170883000024,12.394525741351 M 21.829116999931,6.631339939284 A 17.504802000000 17.504802000000 0.000000000000 0 0 6.496093800000,24.000000000000 A 17.504802000000 17.504802000000 0.000000000000 0 0 24.000000000000,41.503906000000 A 17.504802000000 17.504802000000 0.000000000000 0 0 41.503906000000,24.000000000000 A 17.504802000000 17.504802000000 0.000000000000 0 0 26.170883000441,6.631339964327 A 17.504802000000 17.504802000000 0.000000000000 0 0 24.000000000037,6.496093800000 A 17.504802000000 17.504802000000 0.000000000000 0 0 21.829116999931,6.631339939284"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48"
height="48"
viewBox="0 0 48 48"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg">
<path id="a"
style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
d="m 21.829117,3.5444345 h 4.341766 V 16.502158 H 21.829117 Z M 47,24 A 23,23 0 0 1 24,47 23,23 0 0 1 1,24 23,23 0 0 1 24,1 23,23 0 0 1 47,24 Z"/>
<path id="b"
style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
d="M 24 6.4960938 A 17.504802 17.504802 0 0 0 6.4960938 24 A 17.504802 17.504802 0 0 0 24 41.503906 A 17.504802 17.504802 0 0 0 41.503906 24 A 17.504802 17.504802 0 0 0 24 6.4960938 z M 24 12.193359 A 11.805881 11.805881 0 0 1 35.806641 24 A 11.805881 11.805881 0 0 1 24 35.806641 A 11.805881 11.805881 0 0 1 12.193359 24 A 11.805881 11.805881 0 0 1 24 12.193359 z "/>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M 47.000000000000,24.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 24.000000000000,1.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 1.000000000000,24.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 24.000000000000,47.000000000000 A 23.000000000000 23.000000000000 0.000000000000 0 0 47.000000000000,24.000000000000"/>
</svg>

Before

Width:  |  Height:  |  Size: 716 B

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 34.567256000000,24.000000000000 C 53.715002000000,0.442178810000 45.300906000000,-4.317487500000 24.000000000000,13.432744000000 C -0.827533230000,-4.565426100000 -3.667447600000,0.282647560000 13.432744000000,24.000000000000 C 7.253940300000,36.164106000000 -12.008885000000,59.286948000000 24.000000000000,34.567256000000 C 51.396769000000,54.792375000000 51.194312000000,47.292195000000 34.567256000000,24.000000000000 M 33.482395000000,35.465086000000 C 24.313014000000,27.199681000000 24.313014000000,27.240877000000 14.231274000000,34.296289000000 C 20.811334000000,24.000000000000 21.218447000000,24.000000000000 12.821577000000,12.534583000000 C 24.000000000000,21.897190000000 24.000000000000,22.032673000000 34.786675000000,13.635803000000 C 27.166446000000,22.791080000000 29.683555000000,25.141755000000 33.482395000000,35.465086000000"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 34.567256000000,24.000000000000 C 53.715002000000,0.442178810000 45.300906000000,-4.317487500000 24.000000000000,13.432744000000 C -0.827533230000,-4.565426100000 -3.667447600000,0.282647560000 13.432744000000,24.000000000000 C 7.253940300000,36.164106000000 -12.008885000000,59.286948000000 24.000000000000,34.567256000000 C 51.396769000000,54.792375000000 51.194312000000,47.292195000000 34.567256000000,24.000000000000 M 33.482395000000,35.465086000000 C 24.313014000000,27.199681000000 24.313014000000,27.240877000000 14.231274000000,34.296289000000 C 20.811334000000,24.000000000000 21.218447000000,24.000000000000 12.821577000000,12.534583000000 C 24.000000000000,21.897190000000 24.000000000000,22.032673000000 34.786675000000,13.635803000000 C 27.166446000000,22.791080000000 29.683555000000,25.141755000000 33.482395000000,35.465086000000"/><path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 33.482395000000,35.465086000000 C 29.683555000000,25.141755000000 27.166446000000,22.791080000000 34.786675000000,13.635803000000 C 24.000000000000,22.032673000000 24.000000000000,21.897190000000 12.821577000000,12.534583000000 C 21.218447000000,24.000000000000 20.811334000000,24.000000000000 14.231274000000,34.296289000000 C 24.313014000000,27.240877000000 24.313014000000,27.199681000000 33.482395000000,35.465086000000"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 34.567256000000,24.000000000000 C 53.715002000000,0.442178810000 45.300906000000,-4.317487500000 24.000000000000,13.432744000000 C -0.827533230000,-4.565426100000 -3.667447600000,0.282647560000 13.432744000000,24.000000000000 C 7.253940300000,36.164106000000 -12.008885000000,59.286948000000 24.000000000000,34.567256000000 C 51.396769000000,54.792375000000 51.194312000000,47.292195000000 34.567256000000,24.000000000000 M 33.482395000000,35.465086000000 C 24.313014000000,27.199681000000 24.313014000000,27.240877000000 14.231274000000,34.296289000000 C 20.811334000000,24.000000000000 21.218447000000,24.000000000000 12.821577000000,12.534583000000 C 24.000000000000,21.897190000000 24.000000000000,22.032673000000 34.786675000000,13.635803000000 C 27.166446000000,22.791080000000 29.683555000000,25.141755000000 33.482395000000,35.465086000000"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 34.567256000000,24.000000000000 C 53.715002000000,0.442178810000 45.300906000000,-4.317487500000 24.000000000000,13.432744000000 C -0.827533230000,-4.565426100000 -3.667447600000,0.282647560000 13.432744000000,24.000000000000 C 7.253940300000,36.164106000000 -12.008885000000,59.286948000000 24.000000000000,34.567256000000 C 51.396769000000,54.792375000000 51.194312000000,47.292195000000 34.567256000000,24.000000000000 M 33.482395000000,35.465086000000 C 24.313014000000,27.199681000000 24.313014000000,27.240877000000 14.231274000000,34.296289000000 C 20.811334000000,24.000000000000 21.218447000000,24.000000000000 12.821577000000,12.534583000000 C 24.000000000000,21.897190000000 24.000000000000,22.032673000000 34.786675000000,13.635803000000 C 27.166446000000,22.791080000000 29.683555000000,25.141755000000 33.482395000000,35.465086000000"/><path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 33.482395000000,35.465086000000 C 29.683555000000,25.141755000000 27.166446000000,22.791080000000 34.786675000000,13.635803000000 C 24.000000000000,22.032673000000 24.000000000000,21.897190000000 12.821577000000,12.534583000000 C 21.218447000000,24.000000000000 20.811334000000,24.000000000000 14.231274000000,34.296289000000 C 24.313014000000,27.240877000000 24.313014000000,27.199681000000 33.482395000000,35.465086000000"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 33.482395000000,35.465086000000 C 29.683555000000,25.141755000000 27.166446000000,22.791080000000 34.786675000000,13.635803000000 C 24.000000000000,22.032673000000 24.000000000000,21.897190000000 12.821577000000,12.534583000000 C 21.218447000000,24.000000000000 20.811334000000,24.000000000000 14.231274000000,34.296289000000 C 24.313014000000,27.240877000000 24.313014000000,27.199681000000 33.482395000000,35.465086000000"/>
</svg>

Before

Width:  |  Height:  |  Size: 777 B

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48"
height="48"
viewBox="0 0 48 48"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
>
<path id="a"
style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round"
d="M 34.567256,24 C 51.194312,47.292195 51.396769,54.792375 24,34.567256 -12.008885,59.286948 7.2539403,36.164106 13.432744,24 -3.6674476,0.28264756 -0.82753323,-4.5654261 24,13.432744 45.300906,-4.3174875 53.715002,0.44217881 34.567256,24 Z"/>
<path id="b"
style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round"
d="M 33.482395,35.465086 C 24.313014,27.199681 24.313014,27.240877 14.231274,34.296289 20.811334,24 21.218447,24 12.821577,12.534583 24,21.89719 24,22.032673 34.786675,13.635803 c -7.620229,9.155277 -5.10312,11.505952 -1.30428,21.829283 z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1017 B

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" viewBox="0 0 48 48" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#ff0000;stroke:#000000;stroke-width:1.99937;stroke-linecap:round;stroke-linejoin:round" d="M 34.567256000000,24.000000000000 C 53.715002000000,0.442178810000 45.300906000000,-4.317487500000 24.000000000000,13.432744000000 C -0.827533230000,-4.565426100000 -3.667447600000,0.282647560000 13.432744000000,24.000000000000 C 7.253940300000,36.164106000000 -12.008885000000,59.286948000000 24.000000000000,34.567256000000 C 51.396769000000,54.792375000000 51.194312000000,47.292195000000 34.567256000000,24.000000000000"/>
</svg>

Before

Width:  |  Height:  |  Size: 775 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.5 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 21 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.3 KiB

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="297mm"
height="210mm"
viewBox="0 0 297 210"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
>
<defs
id="defs1"/>
<path
id="a"
style="fill:#ff0000;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round"
d="M 212.67152,105 A 64.171516,64.171516 0 0 1 148.5,169.17152 64.171516,64.171516 0 0 1 84.328484,105 64.171516,64.171516 0 0 1 148.5,40.828484 64.171516,64.171516 0 0 1 212.67152,105 Z"/>
<path
id="b"
style="font-weight:600;font-size:50.8px;font-family:FreeSans;-inkscape-font-specification:'FreeSans Semi-Bold';text-align:center;text-anchor:middle;fill:#ff0000;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round"
d="m 83.755387,112.6962 h -7.62 v 37.0332 h 7.62 z m 22.097973,9.144 c -3.4544,0 -5.892801,1.4732 -7.620001,4.5212 v -4.064 H 91.12136 v 38.5064 h 7.111999 v -14.3256 c 1.7272,3.048 4.165601,4.4704 7.620001,4.4704 6.604,0 11.4808,-6.1976 11.4808,-14.5288 0,-8.0772 -4.3688,-14.5796 -11.4808,-14.5796 z m -1.6256,5.9436 c 3.6068,0 5.9944,3.5052 5.9944,8.7376 0,4.9784 -2.4892,8.4836 -5.9944,8.4836 -3.556,0 -5.994401,-3.4544 -5.994401,-8.5852 0,-5.1308 2.438401,-8.636 5.994401,-8.636 z m 23.62201,13.97 h -6.9596 c 0.2032,5.9944 4.6228,9.144 12.954,9.144 9.6012,0 11.9888,-5.4864 11.9888,-9.2964 0,-3.556 -1.778,-5.842 -5.3848,-6.9088 l -8.9916,-2.5908 c -1.9812,-0.6096 -2.4892,-1.016 -2.4892,-2.1336 0,-1.524 1.6256,-2.54 4.1148,-2.54 3.4036,0 5.08,1.2192 5.1308,3.7084 h 6.858 c -0.1016,-5.7912 -4.572,-9.2964 -11.938,-9.2964 -6.9596,0 -11.2776,3.5052 -11.2776,9.144 0,5.3848 4.4196,6.2484 5.9944,6.7564 l 8.4836,2.6416 c 1.778,0.5588 2.3876,1.1176 2.3876,2.2352 0,1.6764 -1.9812,2.6924 -5.2832,2.6924 -4.4704,0 -5.1816,-1.6764 -5.588,-3.556 z m 47.59959,7.9756 v -27.432 h -7.112 v 17.1704 c 0,3.2512 -2.286,5.3848 -5.7404,5.3848 -3.048,0 -4.572,-1.6256 -4.572,-4.9276 v -17.6276 h -7.112 v 19.1008 c 0,6.0452 3.3528,9.4996 9.1948,9.4996 3.7084,0 6.1976,-1.3716 8.2296,-4.4196 v 3.2512 z m 6.60404,-27.432 v 27.432 h 7.112 v -16.4592 c 0,-3.3528 1.8288,-5.3848 4.8768,-5.3848 2.3876,0 3.8608,1.3716 3.8608,3.556 v 18.288 h 7.112 v -16.4592 c 0,-3.3528 1.8288,-5.3848 4.8768,-5.3848 2.3876,0 3.8608,1.3716 3.8608,3.556 v 18.288 h 7.112 v -19.4056 c 0,-5.334 -3.2512,-8.4836 -8.7376,-8.4836 -3.4544,0 -5.8928,1.2192 -8.0264,4.064 -1.3208,-2.5908 -4.064,-4.064 -7.4676,-4.064 -3.1496,0 -5.1816,1.0668 -7.5184,3.8608 v -3.4036 z M 81.012192,49.196201 h -7.62 v 37.0332 h 25.3492 v -6.35 h -17.7292 z m 35.305978,9.144 c -8.382,0 -13.5128,5.5372 -13.5128,14.5288 0,9.0424 5.1308,14.5288 13.5636,14.5288 8.3312,0 13.5636,-5.5372 13.5636,-14.3256 0,-9.2964 -5.0292,-14.732 -13.6144,-14.732 z m 0.0508,5.7404 c 3.9116,0 6.4516,3.5052 6.4516,8.89 0,5.1308 -2.6416,8.6868 -6.4516,8.6868 -3.8608,0 -6.4516,-3.556 -6.4516,-8.7884 0,-5.2324 2.5908,-8.7884 6.4516,-8.7884 z m 18.89761,-5.2832 v 27.432 h 7.112 v -14.5796 c 0,-4.1656 2.0828,-6.2484 6.2484,-6.2484 0.762,0 1.27,0.0508 2.2352,0.2032 v -7.2136 c -0.4064,-0.0508 -0.6604,-0.0508 -0.8636,-0.0508 -3.2512,0 -6.096,2.1336 -7.62,5.842 v -5.3848 z m 31.34362,-0.4572 c -7.874,0 -12.7,5.6896 -12.7,14.8844 0,8.7884 4.7752,14.1732 12.5476,14.1732 6.14679,0 11.12519,-3.5052 12.69999,-8.89 h -7.0104 c -0.8636,2.1844 -2.8448,3.4544 -5.43559,3.4544 -4.7752,0 -5.5372,-3.5052 -5.6896,-7.2136 h 18.38959 c 0.0508,-0.6096 0.0508,-0.8636 0.0508,-1.2192 0,-12.1412 -7.366,-15.1892 -12.85239,-15.1892 z m 5.43559,11.684 H 161.1238 c 0.4572,-4.1656 2.2352,-6.2484 5.3848,-6.2484 3.30199,0 5.23239,2.2352 5.53719,6.2484 z m 12.75081,-11.2268 v 27.432 h 7.112 v -16.4592 c 0,-3.3528 1.8288,-5.3848 4.8768,-5.3848 2.3876,0 3.8608,1.3716 3.8608,3.556 v 18.288 h 7.112 v -16.4592 c 0,-3.3528 1.8288,-5.3848 4.8768,-5.3848 2.3876,0 3.8608,1.3716 3.8608,3.556 v 18.288 h 7.112 v -19.4056 c 0,-5.334 -3.2512,-8.4836 -8.7376,-8.4836 -3.4544,0 -5.8928,1.2192 -8.0264,4.064 -1.3208,-2.5908 -4.064,-4.064 -7.4676,-4.064 -3.1496,0 -5.1816,1.0668 -7.5184,3.8608 v -3.4036 z"/>
</svg>

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="297mm" height="210mm" viewBox="0 0 297 210" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg">
<defs id="defs1"/>
<path style="fill:#ff0000;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round" d="M 83.755387000000,112.696200000000 L 76.135387000000,112.696200000000 L 76.135387000000,149.729400000000 L 83.755387000000,149.729400000000 L 83.755387000000,112.696200000000 M 210.390827092771,121.956108583236 A 64.171516000000 64.171516000000 0.000000000000 0 0 212.671520000000,105.000000175080 A 64.171516000000 64.171516000000 0.000000000000 0 0 207.758199999817,80.373801093438 L 207.758200000000,69.770201000000 C 207.758200000000,66.417401000000 209.587000000000,64.385401000000 212.635000000000,64.385401000000 C 215.022600000000,64.385401000000 216.495800000000,65.757001000000 216.495800000000,67.941401000000 L 216.495800000000,86.229401000000 L 223.607800000000,86.229401000000 L 223.607800000000,66.823801000000 C 223.607800000000,61.489801000000 220.356600000000,58.340201000000 214.870200000000,58.340201000000 C 211.415800000000,58.340201000000 208.977400000000,59.559401000000 206.843800000000,62.404201000000 C 205.523000000000,59.813401000000 202.779800000000,58.340201000000 199.376200000000,58.340201000000 C 197.292935294555,58.340201000000 195.698620646900,58.806926055163 194.181643223842,59.931482123758 A 64.171516000000 64.171516000000 0.000000000000 0 0 148.500000000000,40.828484000000 A 64.171516000000 64.171516000000 0.000000000000 0 0 89.449691181417,79.879400999017 L 81.012192000000,79.879401000000 L 81.012192000000,49.196201000000 L 73.392192000000,49.196201000000 L 73.392192000000,86.229401000000 L 87.135123409908,86.229401000000 A 64.171516000000 64.171516000000 0.000000000000 0 0 84.328484000000,105.000000000000 A 64.171516000000 64.171516000000 0.000000000000 0 0 91.121360000746,133.734567465873 L 91.121360000000,160.803800000000 L 98.233359000000,160.803800000000 L 98.233359000000,146.478200000000 C 99.523641919633,148.755169858176 101.210821090084,150.124949561489 103.421965352651,150.672252226666 A 64.171516000000 64.171516000000 0.000000000000 0 0 148.500000000000,169.171520000000 A 64.171516000000 64.171516000000 0.000000000000 0 0 197.903000000613,145.955192339229 L 197.903000000000,149.729400000000 L 205.015000000000,149.729400000000 L 205.015000000000,135.398008751254 A 64.171516000000 64.171516000000 0.000000000000 0 0 208.378992717779,128.075752652522 C 208.842066828601,127.950200956146 209.347493127457,127.885400000000 209.891800000000,127.885400000000 C 212.279400000000,127.885400000000 213.752600000000,129.257000000000 213.752600000000,131.441400000000 L 213.752600000000,149.729400000000 L 220.864600000000,149.729400000000 L 220.864600000000,130.323800000000 C 220.864600000000,124.989800000000 217.613400000000,121.840200000000 212.127000000000,121.840200000000 C 211.517996453175,121.840200000000 210.940571159495,121.878093903773 210.390827092639,121.956108583500 M 200.632322706442,67.580689316501 A 64.171516000000 64.171516000000 0.000000000000 0 0 198.381445638899,64.628905814143 C 199.705004967239,65.077751271713 200.518180692801,66.124562475425 200.632322706763,67.580689316459"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 64 KiB

Some files were not shown because too many files have changed in this diff Show More