mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 14:18:04 +08:00
Initial working versions of decimate and vectorize
This commit is contained in:
323
Cargo.lock
generated
323
Cargo.lock
generated
@@ -33,12 +33,24 @@ dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "adler32"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.12"
|
||||
@@ -124,6 +136,15 @@ version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.20"
|
||||
@@ -405,6 +426,17 @@ version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.19",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
@@ -421,7 +453,7 @@ dependencies = [
|
||||
"arrayvec",
|
||||
"log",
|
||||
"nom",
|
||||
"num-rational",
|
||||
"num-rational 0.4.2",
|
||||
"v_frame",
|
||||
]
|
||||
|
||||
@@ -443,7 +475,7 @@ dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"miniz_oxide 0.8.9",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
"windows-targets 0.52.6",
|
||||
@@ -470,9 +502,15 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
"bit-vec 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.8.0"
|
||||
@@ -652,7 +690,7 @@ source = "git+https://github.com/rust-gpu/cargo-gpu?rev=f969528e87baa17a7d48eecf
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_metadata",
|
||||
"clap",
|
||||
"clap 4.5.46",
|
||||
"crossterm",
|
||||
"directories",
|
||||
"env_logger",
|
||||
@@ -814,6 +852,21 @@ dependencies = [
|
||||
"half",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags 1.3.2",
|
||||
"strsim 0.8.0",
|
||||
"textwrap",
|
||||
"unicode-width 0.1.14",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.46"
|
||||
@@ -833,7 +886,7 @@ dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
"strsim 0.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -871,7 +924,7 @@ checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"termcolor",
|
||||
"unicode-width",
|
||||
"unicode-width 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -930,7 +983,7 @@ dependencies = [
|
||||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"unicode-width",
|
||||
"unicode-width 0.2.1",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
@@ -1044,7 +1097,7 @@ dependencies = [
|
||||
"anes",
|
||||
"cast",
|
||||
"ciborium",
|
||||
"clap",
|
||||
"clap 4.5.46",
|
||||
"criterion-plot",
|
||||
"itertools 0.13.0",
|
||||
"num-traits",
|
||||
@@ -1161,6 +1214,16 @@ version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
|
||||
|
||||
[[package]]
|
||||
name = "deflate"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.4.0"
|
||||
@@ -1302,7 +1365,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8de5f62da6c2850fd20cdefef7dd5389121869a88de8af3fda5a20bc849eafdd"
|
||||
dependencies = [
|
||||
"bzip2",
|
||||
"clap",
|
||||
"clap 4.5.46",
|
||||
"indicatif",
|
||||
"regex",
|
||||
"semver",
|
||||
@@ -1497,7 +1560,7 @@ dependencies = [
|
||||
"bit_field",
|
||||
"half",
|
||||
"lebe",
|
||||
"miniz_oxide",
|
||||
"miniz_oxide 0.8.9",
|
||||
"rayon-core",
|
||||
"smallvec",
|
||||
"zune-inflate",
|
||||
@@ -1517,6 +1580,9 @@ name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
@@ -1562,7 +1628,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
"miniz_oxide 0.8.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flo_curves"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c227ffb4f98baa9073c033e23243089d0ed3dd1f5cd620894452074db48a29b"
|
||||
dependencies = [
|
||||
"itertools 0.8.2",
|
||||
"roots 0.0.6",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1882,6 +1959,16 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06"
|
||||
dependencies = [
|
||||
"color_quant",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.13.3"
|
||||
@@ -2060,7 +2147,7 @@ name = "graphene-cli"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"clap 4.5.46",
|
||||
"fern",
|
||||
"futures",
|
||||
"graph-craft",
|
||||
@@ -2085,7 +2172,7 @@ dependencies = [
|
||||
"dyn-any",
|
||||
"glam",
|
||||
"graphene-core-shaders",
|
||||
"image",
|
||||
"image 0.25.6",
|
||||
"kurbo",
|
||||
"log",
|
||||
"lyon_geom",
|
||||
@@ -2103,6 +2190,8 @@ dependencies = [
|
||||
"specta",
|
||||
"tinyvec",
|
||||
"tokio",
|
||||
"visioncortex",
|
||||
"vtracer",
|
||||
"wgpu",
|
||||
]
|
||||
|
||||
@@ -2163,7 +2252,7 @@ dependencies = [
|
||||
"graphene-core",
|
||||
"graphene-core-shaders",
|
||||
"graphene-raster-nodes-shaders",
|
||||
"image",
|
||||
"image 0.25.6",
|
||||
"kurbo",
|
||||
"ndarray",
|
||||
"node-macro",
|
||||
@@ -2209,7 +2298,7 @@ dependencies = [
|
||||
"graphene-path-bool",
|
||||
"graphene-raster-nodes",
|
||||
"graphene-svg-renderer",
|
||||
"image",
|
||||
"image 0.25.6",
|
||||
"log",
|
||||
"node-macro",
|
||||
"reqwest",
|
||||
@@ -2287,7 +2376,7 @@ dependencies = [
|
||||
"graph-craft",
|
||||
"graphene-std",
|
||||
"graphite-editor",
|
||||
"image",
|
||||
"image 0.25.6",
|
||||
"ron",
|
||||
"serde",
|
||||
"thiserror 2.0.16",
|
||||
@@ -2311,7 +2400,7 @@ dependencies = [
|
||||
"graph-craft",
|
||||
"graphene-std",
|
||||
"graphite-proc-macros",
|
||||
"image",
|
||||
"image 0.25.6",
|
||||
"interpreted-executor",
|
||||
"js-sys",
|
||||
"kurbo",
|
||||
@@ -2422,6 +2511,15 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.5.2"
|
||||
@@ -2740,6 +2838,25 @@ dependencies = [
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.23.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"color_quant",
|
||||
"gif 0.11.4",
|
||||
"jpeg-decoder 0.1.22",
|
||||
"num-iter",
|
||||
"num-rational 0.3.2",
|
||||
"num-traits",
|
||||
"png 0.16.8",
|
||||
"scoped_threadpool",
|
||||
"tiff 0.6.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.25.6"
|
||||
@@ -2750,15 +2867,15 @@ dependencies = [
|
||||
"byteorder-lite",
|
||||
"color_quant",
|
||||
"exr",
|
||||
"gif",
|
||||
"gif 0.13.3",
|
||||
"image-webp 0.2.4",
|
||||
"num-traits",
|
||||
"png",
|
||||
"png 0.17.16",
|
||||
"qoi",
|
||||
"ravif",
|
||||
"rayon",
|
||||
"rgb",
|
||||
"tiff",
|
||||
"tiff 0.9.1",
|
||||
"zune-core",
|
||||
"zune-jpeg",
|
||||
]
|
||||
@@ -2832,7 +2949,7 @@ checksum = "70a646d946d06bedbbc4cac4c218acf4bbf2d87757a784857025f4d447e4e1cd"
|
||||
dependencies = [
|
||||
"console",
|
||||
"portable-atomic",
|
||||
"unicode-width",
|
||||
"unicode-width 0.2.1",
|
||||
"unit-prefix",
|
||||
"web-time",
|
||||
]
|
||||
@@ -2946,6 +3063,15 @@ version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
@@ -3026,6 +3152,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2"
|
||||
dependencies = [
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.3.2"
|
||||
@@ -3329,6 +3464,25 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
@@ -3577,6 +3731,28 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.2"
|
||||
@@ -3941,11 +4117,11 @@ dependencies = [
|
||||
"criterion",
|
||||
"glam",
|
||||
"glob",
|
||||
"image",
|
||||
"image 0.25.6",
|
||||
"lyon_geom",
|
||||
"regex",
|
||||
"resvg",
|
||||
"roots",
|
||||
"roots 0.0.8",
|
||||
"rustc-hash 2.1.1",
|
||||
"slotmap",
|
||||
"smallvec",
|
||||
@@ -4124,6 +4300,18 @@ dependencies = [
|
||||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.16.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"crc32fast",
|
||||
"deflate",
|
||||
"miniz_oxide 0.3.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.16"
|
||||
@@ -4134,7 +4322,7 @@ dependencies = [
|
||||
"crc32fast",
|
||||
"fdeflate",
|
||||
"flate2",
|
||||
"miniz_oxide",
|
||||
"miniz_oxide 0.8.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4145,7 +4333,7 @@ checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"concurrent-queue",
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.5.2",
|
||||
"pin-project-lite",
|
||||
"rustix",
|
||||
"windows-sys 0.60.2",
|
||||
@@ -4673,7 +4861,7 @@ version = "0.44.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a325d5e8d1cebddd070b13f44cec8071594ab67d1012797c121f27a669b7958"
|
||||
dependencies = [
|
||||
"gif",
|
||||
"gif 0.13.3",
|
||||
"image-webp 0.1.3",
|
||||
"log",
|
||||
"pico-args",
|
||||
@@ -4744,6 +4932,12 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roots"
|
||||
version = "0.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84348444bd7ad45729d0c49a4240d7cdc11c9d512c06c5ad1835c1ad4acda6db"
|
||||
|
||||
[[package]]
|
||||
name = "roots"
|
||||
version = "0.0.8"
|
||||
@@ -4941,6 +5135,12 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
||||
|
||||
[[package]]
|
||||
name = "scoped_threadpool"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
@@ -5330,7 +5530,7 @@ version = "0.9.0"
|
||||
source = "git+https://github.com/rust-gpu/rust-gpu?rev=c12f216121820580731440ee79ebc7403d6ea04f#c12f216121820580731440ee79ebc7403d6ea04f"
|
||||
dependencies = [
|
||||
"cargo_metadata",
|
||||
"clap",
|
||||
"clap 4.5.46",
|
||||
"log",
|
||||
"memchr",
|
||||
"notify",
|
||||
@@ -5394,6 +5594,12 @@ dependencies = [
|
||||
"float-cmp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
@@ -5597,6 +5803,15 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width 0.1.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
@@ -5646,6 +5861,17 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiff"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437"
|
||||
dependencies = [
|
||||
"jpeg-decoder 0.1.22",
|
||||
"miniz_oxide 0.4.4",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiff"
|
||||
version = "0.9.1"
|
||||
@@ -5653,7 +5879,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"jpeg-decoder",
|
||||
"jpeg-decoder 0.3.2",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
@@ -5699,7 +5925,7 @@ dependencies = [
|
||||
"bytemuck",
|
||||
"cfg-if",
|
||||
"log",
|
||||
"png",
|
||||
"png 0.17.16",
|
||||
"tiny-skia-path",
|
||||
]
|
||||
|
||||
@@ -6090,6 +6316,12 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.1"
|
||||
@@ -6261,6 +6493,12 @@ version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "vello"
|
||||
version = "0.5.0"
|
||||
@@ -6270,7 +6508,7 @@ dependencies = [
|
||||
"futures-intrusive",
|
||||
"log",
|
||||
"peniko",
|
||||
"png",
|
||||
"png 0.17.16",
|
||||
"skrifa 0.31.3",
|
||||
"static_assertions",
|
||||
"thiserror 2.0.16",
|
||||
@@ -6315,6 +6553,29 @@ version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "visioncortex"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dbe154fc281d74b2322adc18de7f9bbb9c4318d58ef6c201007f3fd99df9fff"
|
||||
dependencies = [
|
||||
"bit-vec 0.6.3",
|
||||
"flo_curves",
|
||||
"log",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vtracer"
|
||||
version = "0.6.4"
|
||||
source = "git+https://github.com/otdavies/vtracer?rev=31a8b270f6ff825955cf3af888eae6fbfd36e146#31a8b270f6ff825955cf3af888eae6fbfd36e146"
|
||||
dependencies = [
|
||||
"clap 2.34.0",
|
||||
"fastrand",
|
||||
"image 0.23.14",
|
||||
"visioncortex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
@@ -6626,7 +6887,7 @@ checksum = "f7b882196f8368511d613c6aeec80655160db6646aebddf8328879a88d54e500"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bit-set",
|
||||
"bit-vec",
|
||||
"bit-vec 0.8.0",
|
||||
"bitflags 2.9.3",
|
||||
"bytemuck",
|
||||
"cfg_aliases",
|
||||
|
||||
@@ -56,6 +56,9 @@ default-members = [
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
[patch.crates-io]
|
||||
vtracer = { git = "https://github.com/otdavies/vtracer", rev = "31a8b270f6ff825955cf3af888eae6fbfd36e146" }
|
||||
|
||||
[workspace.dependencies]
|
||||
# Local dependencies
|
||||
dyn-any = { path = "libraries/dyn-any", features = [
|
||||
@@ -195,9 +198,14 @@ open = "5.3"
|
||||
poly-cool = "0.3"
|
||||
spin = "0.10"
|
||||
clap = "4.5"
|
||||
# Set compile type to rlib for wasm compatibility
|
||||
visioncortex = { version = "0.8.8" }
|
||||
vtracer = { version = "0.6.4" }
|
||||
spirv-std = { git = "https://github.com/rust-gpu/rust-gpu", rev = "c12f216121820580731440ee79ebc7403d6ea04f", features = ["bytemuck"] }
|
||||
cargo-gpu = { git = "https://github.com/rust-gpu/cargo-gpu", rev = "f969528e87baa17a7d48eecf4a6fcfdcaaf30566" }
|
||||
|
||||
|
||||
|
||||
[workspace.lints.rust]
|
||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(target_arch, values("spirv"))'] }
|
||||
|
||||
|
||||
@@ -215,6 +215,7 @@ pub(crate) fn property_from_type(
|
||||
Some(x) if x == TypeId::of::<StrokeAlign>() => enum_choice::<StrokeAlign>().for_socket(default_info).property_row(),
|
||||
Some(x) if x == TypeId::of::<PaintOrder>() => enum_choice::<PaintOrder>().for_socket(default_info).property_row(),
|
||||
Some(x) if x == TypeId::of::<ArcType>() => enum_choice::<ArcType>().for_socket(default_info).property_row(),
|
||||
// Add here
|
||||
Some(x) if x == TypeId::of::<TextAlign>() => enum_choice::<TextAlign>().for_socket(default_info).property_row(),
|
||||
Some(x) if x == TypeId::of::<MergeByDistanceAlgorithm>() => enum_choice::<MergeByDistanceAlgorithm>().for_socket(default_info).property_row(),
|
||||
Some(x) if x == TypeId::of::<PointSpacingType>() => enum_choice::<PointSpacingType>().for_socket(default_info).property_row(),
|
||||
|
||||
@@ -40,6 +40,8 @@ lyon_geom = { workspace = true }
|
||||
log = { workspace = true }
|
||||
base64 = { workspace = true }
|
||||
poly-cool = { workspace = true }
|
||||
vtracer = { workspace = true }
|
||||
visioncortex = { workspace = true }
|
||||
|
||||
# Optional workspace dependencies
|
||||
serde = { workspace = true, optional = true }
|
||||
|
||||
62
node-graph/gcore/src/vector/algorithms/decimation.rs
Normal file
62
node-graph/gcore/src/vector/algorithms/decimation.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use glam::DVec2;
|
||||
|
||||
/// Implements the Ramer-Douglas-Peucker algorithm to find indices of points to keep.
|
||||
pub fn ramer_douglas_peucker(points: &[DVec2], epsilon: f64) -> Vec<usize> {
|
||||
if points.len() <= 2 {
|
||||
return (0..points.len()).collect();
|
||||
}
|
||||
|
||||
let mut kept_indices = Vec::new();
|
||||
rdp_recursive(points, 0, points.len() - 1, epsilon, &mut kept_indices);
|
||||
kept_indices.sort_unstable();
|
||||
kept_indices.dedup();
|
||||
kept_indices
|
||||
}
|
||||
|
||||
pub fn rdp_recursive(points: &[DVec2], start_idx: usize, end_idx: usize, epsilon: f64, kept_indices: &mut Vec<usize>) {
|
||||
if start_idx >= end_idx {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut dmax = 0.0;
|
||||
let mut index = start_idx;
|
||||
|
||||
// Find the point with maximum perpendicular distance from the line segment
|
||||
for i in (start_idx + 1)..end_idx {
|
||||
let d = perpendicular_distance(points[i], points[start_idx], points[end_idx]);
|
||||
if d > dmax {
|
||||
index = i;
|
||||
dmax = d;
|
||||
}
|
||||
}
|
||||
|
||||
// If max distance is greater than epsilon, recursively simplify
|
||||
if dmax > epsilon {
|
||||
rdp_recursive(points, start_idx, index, epsilon, kept_indices);
|
||||
rdp_recursive(points, index, end_idx, epsilon, kept_indices);
|
||||
} else {
|
||||
// Keep only the endpoints
|
||||
kept_indices.push(start_idx);
|
||||
kept_indices.push(end_idx);
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the perpendicular distance from a point to a line defined by two endpoints.
|
||||
pub fn perpendicular_distance(point: DVec2, line_start: DVec2, line_end: DVec2) -> f64 {
|
||||
let dx = line_end.x - line_start.x;
|
||||
let dy = line_end.y - line_start.y;
|
||||
|
||||
let line_length_squared = dx * dx + dy * dy;
|
||||
|
||||
// If the line segment is actually a point, return the distance to that point
|
||||
if line_length_squared == 0.0 {
|
||||
return point.distance(line_start);
|
||||
}
|
||||
|
||||
// Calculate perpendicular distance using the cross product formula:
|
||||
// distance = |dy·px - dx·py + x2·y1 - y2·x1| / √(dx² + dy²)
|
||||
let numerator = (dy * point.x - dx * point.y + line_end.x * line_start.y - line_end.y * line_start.x).abs();
|
||||
let denominator = line_length_squared.sqrt();
|
||||
|
||||
numerator / denominator
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod bezpath_algorithms;
|
||||
mod contants;
|
||||
pub mod decimation;
|
||||
pub mod instance;
|
||||
pub mod intersection;
|
||||
pub mod merge_by_distance;
|
||||
@@ -7,3 +8,4 @@ pub mod offset_subpath;
|
||||
pub mod poisson_disk;
|
||||
pub mod spline;
|
||||
pub mod util;
|
||||
pub mod vectorize;
|
||||
|
||||
357
node-graph/gcore/src/vector/algorithms/vectorize.rs
Normal file
357
node-graph/gcore/src/vector/algorithms/vectorize.rs
Normal file
@@ -0,0 +1,357 @@
|
||||
use crate::{
|
||||
Graphic,
|
||||
raster_types::{CPU, Raster},
|
||||
table::{Table, TableRow, TableRowRef},
|
||||
vector::Vector,
|
||||
vector::VectorExt,
|
||||
vector::style::Fill,
|
||||
};
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphene_core_shaders::color::Color;
|
||||
use kurbo::{BezPath, PathEl};
|
||||
use visioncortex::PathSimplifyMode;
|
||||
use vtracer::{ColorMode, Config, Hierarchical};
|
||||
|
||||
/// Parses SVG path data and appends it to a Vector
|
||||
pub fn parse_svg_paths_to_vector(svg_content: &str) -> Vec<TableRow<Vector>> {
|
||||
let mut rows = Vec::new();
|
||||
|
||||
for element in extract_path_elements(svg_content) {
|
||||
let attributes = parse_path_attributes(&element);
|
||||
let Some(path_data) = attribute_value(&attributes, "d") else { continue };
|
||||
let Ok(bezpath) = BezPath::from_svg(path_data) else { continue };
|
||||
|
||||
let fill = parse_fill_attribute(attribute_value(&attributes, "fill"));
|
||||
let transform = parse_transform_attribute(attribute_value(&attributes, "transform"));
|
||||
|
||||
for subpath in split_bezpath_subpaths(&bezpath) {
|
||||
if subpath.elements().is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut vector = Vector::default();
|
||||
vector.append_bezpath(subpath);
|
||||
vector.style.set_fill(fill.clone());
|
||||
|
||||
let mut table_row = TableRow::new_from_element(vector);
|
||||
table_row.transform = transform;
|
||||
rows.push(table_row);
|
||||
}
|
||||
}
|
||||
|
||||
rows
|
||||
}
|
||||
|
||||
fn split_bezpath_subpaths(bezpath: &BezPath) -> Vec<BezPath> {
|
||||
let mut subpaths = Vec::new();
|
||||
let mut current = BezPath::new();
|
||||
|
||||
for element in bezpath.elements() {
|
||||
match element {
|
||||
PathEl::MoveTo(_) => {
|
||||
if !current.is_empty() {
|
||||
subpaths.push(std::mem::take(&mut current));
|
||||
}
|
||||
current.push(*element);
|
||||
}
|
||||
PathEl::ClosePath => {
|
||||
current.close_path();
|
||||
subpaths.push(std::mem::take(&mut current));
|
||||
}
|
||||
_ => current.push(*element),
|
||||
}
|
||||
}
|
||||
|
||||
if !current.is_empty() {
|
||||
subpaths.push(current);
|
||||
}
|
||||
|
||||
subpaths
|
||||
}
|
||||
|
||||
pub fn extract_path_elements(svg_content: &str) -> Vec<String> {
|
||||
let mut elements = Vec::new();
|
||||
let mut search_start = 0;
|
||||
|
||||
while let Some(relative_start) = svg_content[search_start..].find("<path") {
|
||||
let start = search_start + relative_start;
|
||||
let mut index = start + "<path".len();
|
||||
let mut in_quotes = false;
|
||||
let mut quote_char = '\0';
|
||||
|
||||
while index < svg_content.len() {
|
||||
let ch = svg_content.as_bytes()[index] as char;
|
||||
|
||||
match ch {
|
||||
'"' | '\'' if !in_quotes => {
|
||||
in_quotes = true;
|
||||
quote_char = ch;
|
||||
}
|
||||
c if c == quote_char && in_quotes => {
|
||||
in_quotes = false;
|
||||
}
|
||||
'>' if !in_quotes => {
|
||||
elements.push(svg_content[start..=index].to_string());
|
||||
search_start = index + 1;
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if index >= svg_content.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
elements
|
||||
}
|
||||
|
||||
pub fn is_attribute_name_char(ch: char) -> bool {
|
||||
ch.is_ascii_alphanumeric() || matches!(ch, '-' | '_' | ':' | '.')
|
||||
}
|
||||
|
||||
pub fn parse_path_attributes(element: &str) -> Vec<(String, String)> {
|
||||
let mut attributes = Vec::new();
|
||||
let mut body = element.trim();
|
||||
|
||||
if let Some(start) = body.find("<path") {
|
||||
body = &body[start + "<path".len()..];
|
||||
} else {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
if let Some(end) = body.rfind('>') {
|
||||
body = &body[..end];
|
||||
}
|
||||
|
||||
body = body.trim();
|
||||
if let Some(stripped) = body.strip_suffix('/') {
|
||||
body = stripped.trim_end();
|
||||
}
|
||||
|
||||
let bytes = body.as_bytes();
|
||||
let mut index = 0;
|
||||
|
||||
while index < bytes.len() {
|
||||
while index < bytes.len() && bytes[index].is_ascii_whitespace() {
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if index >= bytes.len() || bytes[index] == b'/' {
|
||||
break;
|
||||
}
|
||||
|
||||
let name_start = index;
|
||||
while index < bytes.len() && is_attribute_name_char(body.as_bytes()[index] as char) {
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if name_start == index {
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
let name = body[name_start..index].trim();
|
||||
|
||||
while index < bytes.len() && bytes[index].is_ascii_whitespace() {
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if index >= bytes.len() || bytes[index] != b'=' {
|
||||
while index < bytes.len() && !bytes[index].is_ascii_whitespace() && bytes[index] != b'/' {
|
||||
index += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
index += 1;
|
||||
while index < bytes.len() && bytes[index].is_ascii_whitespace() {
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if index >= bytes.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
let mut value = String::new();
|
||||
|
||||
if bytes[index] == b'"' || bytes[index] == b'\'' {
|
||||
let quote = bytes[index];
|
||||
index += 1;
|
||||
let value_start = index;
|
||||
|
||||
while index < bytes.len() && bytes[index] != quote {
|
||||
index += 1;
|
||||
}
|
||||
|
||||
value.push_str(body[value_start..index.min(bytes.len())].trim());
|
||||
if index < bytes.len() {
|
||||
index += 1;
|
||||
}
|
||||
} else {
|
||||
let value_start = index;
|
||||
while index < bytes.len() && !bytes[index].is_ascii_whitespace() && bytes[index] != b'/' {
|
||||
index += 1;
|
||||
}
|
||||
value.push_str(body[value_start..index].trim());
|
||||
}
|
||||
|
||||
attributes.push((name.to_string(), value));
|
||||
}
|
||||
|
||||
attributes
|
||||
}
|
||||
|
||||
pub fn attribute_value<'a>(attributes: &'a [(String, String)], name: &str) -> Option<&'a str> {
|
||||
attributes.iter().find_map(|(key, value)| (key == name).then_some(value.as_str()))
|
||||
}
|
||||
|
||||
pub fn parse_fill_attribute(value: Option<&str>) -> Fill {
|
||||
let Some(value) = value.map(str::trim) else {
|
||||
return Fill::None;
|
||||
};
|
||||
|
||||
if value.eq_ignore_ascii_case("none") {
|
||||
return Fill::None;
|
||||
}
|
||||
|
||||
if let Some(hex) = value.strip_prefix('#') {
|
||||
if let Some(color) = Color::from_rgb_str(hex) {
|
||||
return Fill::Solid(color);
|
||||
}
|
||||
} else if let Some(color) = Color::from_rgb_str(value) {
|
||||
return Fill::Solid(color);
|
||||
}
|
||||
|
||||
Fill::None
|
||||
}
|
||||
|
||||
pub fn transform_from_function(name: &str, arguments: &[f64]) -> DAffine2 {
|
||||
match name {
|
||||
"translate" => match arguments.len() {
|
||||
0 => DAffine2::IDENTITY,
|
||||
1 => DAffine2::from_translation(DVec2::new(arguments[0], 0.)),
|
||||
_ => DAffine2::from_translation(DVec2::new(arguments[0], arguments[1])),
|
||||
},
|
||||
"scale" => match arguments.len() {
|
||||
0 => DAffine2::IDENTITY,
|
||||
1 => DAffine2::from_scale(DVec2::splat(arguments[0])),
|
||||
_ => DAffine2::from_scale(DVec2::new(arguments[0], arguments[1])),
|
||||
},
|
||||
"rotate" => {
|
||||
if arguments.is_empty() {
|
||||
return DAffine2::IDENTITY;
|
||||
}
|
||||
|
||||
let angle = arguments[0].to_radians();
|
||||
if arguments.len() >= 3 {
|
||||
let center = DVec2::new(arguments[1], arguments[2]);
|
||||
DAffine2::from_translation(center) * DAffine2::from_angle(angle) * DAffine2::from_translation(-center)
|
||||
} else {
|
||||
DAffine2::from_angle(angle)
|
||||
}
|
||||
}
|
||||
"matrix" if arguments.len() == 6 => DAffine2::from_cols_array(&[arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]]),
|
||||
_ => DAffine2::IDENTITY,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_transform_attribute(value: Option<&str>) -> DAffine2 {
|
||||
let Some(value) = value.map(str::trim) else {
|
||||
return DAffine2::IDENTITY;
|
||||
};
|
||||
let mut transform = DAffine2::IDENTITY;
|
||||
let mut remaining = value;
|
||||
|
||||
while let Some(open_paren) = remaining.find('(') {
|
||||
let (name_part, after_name) = remaining.split_at(open_paren);
|
||||
let name = name_part.trim();
|
||||
let after_name = &after_name[1..];
|
||||
|
||||
if let Some(close_paren) = after_name.find(')') {
|
||||
let arguments = after_name[..close_paren]
|
||||
.split(|c| matches!(c, ',' | ' ' | '\t'))
|
||||
.filter_map(|token| {
|
||||
let trimmed = token.trim();
|
||||
if trimmed.is_empty() { None } else { trimmed.parse::<f64>().ok() }
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
transform = transform * transform_from_function(name, &arguments);
|
||||
remaining = &after_name[close_paren + 1..];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
transform
|
||||
}
|
||||
|
||||
pub fn color_mode_from_u32(value: u32) -> ColorMode {
|
||||
match value {
|
||||
0 => ColorMode::Color,
|
||||
1 => ColorMode::Binary,
|
||||
_ => ColorMode::Color,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hierarchical_from_u32(value: u32) -> Hierarchical {
|
||||
match value {
|
||||
0 => Hierarchical::Stacked,
|
||||
1 => Hierarchical::Cutout,
|
||||
_ => Hierarchical::Stacked,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn simplify_mode_from_u32(value: u32) -> PathSimplifyMode {
|
||||
match value {
|
||||
0 => PathSimplifyMode::None,
|
||||
1 => PathSimplifyMode::Polygon,
|
||||
2 => PathSimplifyMode::Spline,
|
||||
_ => PathSimplifyMode::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_vectorize_config(
|
||||
color_mode: u32,
|
||||
hierarchical: u32,
|
||||
path_simplify_mode: u32,
|
||||
filter_speckle: u32,
|
||||
color_precision: u32,
|
||||
layer_difference: u32,
|
||||
corner_threshold: f32,
|
||||
length_threshold: f64,
|
||||
max_iterations: u32,
|
||||
splice_threshold: f32,
|
||||
path_precision: u32,
|
||||
) -> Config {
|
||||
Config {
|
||||
color_mode: color_mode_from_u32(color_mode),
|
||||
hierarchical: hierarchical_from_u32(hierarchical),
|
||||
mode: simplify_mode_from_u32(path_simplify_mode),
|
||||
filter_speckle: filter_speckle as usize,
|
||||
color_precision: color_precision as i32,
|
||||
layer_difference: layer_difference as i32,
|
||||
corner_threshold: corner_threshold as i32,
|
||||
length_threshold,
|
||||
max_iterations: max_iterations as usize,
|
||||
splice_threshold: splice_threshold as i32,
|
||||
path_precision: Some(path_precision),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vector_row_to_graphic_row(mut vector_row: TableRow<Vector>, source_row: &TableRowRef<'_, Raster<CPU>>) -> TableRow<Graphic> {
|
||||
vector_row.transform = *source_row.transform * vector_row.transform;
|
||||
vector_row.alpha_blending = source_row.alpha_blending.clone();
|
||||
vector_row.source_node_id = source_row.source_node_id.clone();
|
||||
|
||||
TableRow {
|
||||
element: Graphic::Vector(Table::new_from_row(vector_row)),
|
||||
transform: DAffine2::IDENTITY,
|
||||
alpha_blending: Default::default(),
|
||||
source_node_id: Default::default(),
|
||||
}
|
||||
}
|
||||
@@ -74,6 +74,8 @@ pub enum ArcType {
|
||||
PieSlice,
|
||||
}
|
||||
|
||||
// Add enum here (widget Dropdown vs Radio)
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, Hash, DynAny, specta::Type, node_macro::ChoiceType)]
|
||||
#[widget(Radio)]
|
||||
|
||||
@@ -12,7 +12,9 @@ use crate::table::{Table, TableRow, TableRowMut};
|
||||
use crate::transform::{Footprint, ReferencePoint, Transform};
|
||||
use crate::vector::PointDomain;
|
||||
use crate::vector::algorithms::bezpath_algorithms::eval_pathseg_euclidean;
|
||||
use crate::vector::algorithms::decimation::ramer_douglas_peucker;
|
||||
use crate::vector::algorithms::merge_by_distance::MergeByDistanceExt;
|
||||
use crate::vector::algorithms::vectorize::{build_vectorize_config, parse_svg_paths_to_vector, vector_row_to_graphic_row};
|
||||
use crate::vector::misc::{MergeByDistanceAlgorithm, PointSpacingType, is_linear};
|
||||
use crate::vector::misc::{handles_to_segment, segment_to_handles};
|
||||
use crate::vector::style::{PaintOrder, StrokeAlign, StrokeCap, StrokeJoin};
|
||||
@@ -202,6 +204,177 @@ where
|
||||
content
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Raster: Filter"), path(graphene_core::vector))]
|
||||
async fn vectorize(
|
||||
_: impl Ctx,
|
||||
image: Table<Raster<CPU>>,
|
||||
color_mode: u32,
|
||||
hierarchical: u32,
|
||||
path_simplify_mode: u32,
|
||||
#[default(4)]
|
||||
#[hard_min(1.)]
|
||||
filter_speckle: u32,
|
||||
#[default(6)]
|
||||
#[hard_min(1.)]
|
||||
#[hard_max(8.)]
|
||||
color_precision: u32,
|
||||
#[default(16)]
|
||||
#[hard_min(0.)]
|
||||
#[hard_max(255.)]
|
||||
layer_difference: u32,
|
||||
#[default(60)]
|
||||
#[hard_min(0.)]
|
||||
#[hard_max(180.)]
|
||||
corner_threshold: f32,
|
||||
#[default(4.)]
|
||||
#[hard_min(3.5)]
|
||||
length_threshold: f64,
|
||||
#[default(10)]
|
||||
#[hard_min(1.)]
|
||||
max_iterations: u32,
|
||||
#[default(45)]
|
||||
#[hard_min(0.)]
|
||||
#[hard_max(180.)]
|
||||
splice_threshold: f32,
|
||||
#[default(8)]
|
||||
#[hard_min(0.)]
|
||||
path_precision: u32,
|
||||
) -> Table<Graphic> {
|
||||
let mut result_table = Table::new();
|
||||
|
||||
for row in image.iter() {
|
||||
let raster = &row.element;
|
||||
|
||||
if raster.width == 0 || raster.height == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (pixel_data, width, height) = raster.to_flat_u8();
|
||||
let color_image = vtracer::ColorImage {
|
||||
pixels: pixel_data,
|
||||
width: width as usize,
|
||||
height: height as usize,
|
||||
};
|
||||
|
||||
let config = build_vectorize_config(
|
||||
color_mode,
|
||||
hierarchical,
|
||||
path_simplify_mode,
|
||||
filter_speckle,
|
||||
color_precision,
|
||||
layer_difference,
|
||||
corner_threshold,
|
||||
length_threshold,
|
||||
max_iterations,
|
||||
splice_threshold,
|
||||
path_precision,
|
||||
);
|
||||
|
||||
match vtracer::convert(color_image, config) {
|
||||
Ok(svg_file) => {
|
||||
let svg_content = svg_file.to_string();
|
||||
log::trace!("Generated SVG content:\n{}", svg_content);
|
||||
|
||||
let vector_rows = parse_svg_paths_to_vector(&svg_content);
|
||||
|
||||
let mut graphic_table = Table::new();
|
||||
for vector_row in vector_rows {
|
||||
graphic_table.push(vector_row_to_graphic_row(vector_row, &row));
|
||||
}
|
||||
|
||||
let graphic_group = Graphic::Graphic(graphic_table);
|
||||
let mut graphic_row = TableRow::new_from_element(graphic_group);
|
||||
graphic_row.transform = *row.transform;
|
||||
graphic_row.alpha_blending = *row.alpha_blending;
|
||||
graphic_row.source_node_id = *row.source_node_id;
|
||||
result_table.push(graphic_row);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Vectorization failed: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result_table
|
||||
}
|
||||
|
||||
/// Simplifies vector paths using the Ramer-Douglas-Peucker algorithm for line decimation.
|
||||
/// Reduces the number of points in a path while preserving its overall shape within a specified tolerance.
|
||||
#[node_macro::node(category("Vector: Modifier"), path(graphene_core::vector))]
|
||||
fn decimate(
|
||||
_: impl Ctx,
|
||||
source: Table<Vector>,
|
||||
#[default(0.1)]
|
||||
#[hard_min(0.001)]
|
||||
tolerance: f64,
|
||||
) -> Table<Vector> {
|
||||
use crate::vector::misc::{dvec2_to_point, point_to_dvec2};
|
||||
use kurbo::Affine;
|
||||
|
||||
source
|
||||
.into_iter()
|
||||
.map(|row| {
|
||||
let transform = Affine::new(row.transform.to_cols_array());
|
||||
let vector = row.element;
|
||||
|
||||
let mut new_vector = Vector {
|
||||
style: vector.style.clone(),
|
||||
upstream_nested_layers: vector.upstream_nested_layers.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Process each bezpath in the vector
|
||||
for mut bezpath in vector.stroke_bezpath_iter() {
|
||||
// Apply transform to work in world space for accurate distance calculations
|
||||
bezpath.apply_affine(transform);
|
||||
|
||||
// Extract anchor points from the bezpath
|
||||
let anchors: Vec<DVec2> = bezpath.segments().map(|seg| point_to_dvec2(seg.start())).collect();
|
||||
|
||||
// Handle the last point if the path isn't closed
|
||||
let is_closed = bezpath.elements().last() == Some(&kurbo::PathEl::ClosePath);
|
||||
let mut all_anchors = anchors;
|
||||
if !is_closed {
|
||||
if let Some(last_seg) = bezpath.segments().last() {
|
||||
all_anchors.push(point_to_dvec2(last_seg.end()));
|
||||
}
|
||||
}
|
||||
|
||||
if all_anchors.len() <= 2 {
|
||||
// Can't simplify paths with 2 or fewer points
|
||||
bezpath.apply_affine(transform.inverse());
|
||||
new_vector.append_bezpath(bezpath);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Apply RDP algorithm
|
||||
let simplified_indices = ramer_douglas_peucker(&all_anchors, tolerance);
|
||||
|
||||
// Build new bezpath from simplified points
|
||||
let mut simplified_bezpath = kurbo::BezPath::new();
|
||||
if let Some(&first_idx) = simplified_indices.first() {
|
||||
simplified_bezpath.move_to(dvec2_to_point(all_anchors[first_idx]));
|
||||
|
||||
for &idx in simplified_indices.iter().skip(1) {
|
||||
simplified_bezpath.line_to(dvec2_to_point(all_anchors[idx]));
|
||||
}
|
||||
|
||||
// Close the path if the original was closed and we have enough points
|
||||
if is_closed && simplified_indices.len() > 2 {
|
||||
simplified_bezpath.close_path();
|
||||
}
|
||||
}
|
||||
|
||||
// Transform back to local space
|
||||
simplified_bezpath.apply_affine(transform.inverse());
|
||||
new_vector.append_bezpath(simplified_bezpath);
|
||||
}
|
||||
|
||||
TableRow { element: new_vector, ..row }
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Instancing"), path(graphene_core::vector))]
|
||||
async fn repeat<I: 'n + Send + Clone>(
|
||||
_: impl Ctx,
|
||||
|
||||
@@ -240,6 +240,7 @@ tagged_value! {
|
||||
SelectiveColorChoice(graphene_raster_nodes::adjustments::SelectiveColorChoice),
|
||||
GridType(graphene_core::vector::misc::GridType),
|
||||
ArcType(graphene_core::vector::misc::ArcType),
|
||||
// Add here
|
||||
MergeByDistanceAlgorithm(graphene_core::vector::misc::MergeByDistanceAlgorithm),
|
||||
PointSpacingType(graphene_core::vector::misc::PointSpacingType),
|
||||
SpiralType(graphene_core::vector::misc::SpiralType),
|
||||
|
||||
@@ -132,6 +132,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::SelectiveColorChoice]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::GridType]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::ArcType]),
|
||||
// Add here
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::MergeByDistanceAlgorithm]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::PointSpacingType]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::style::FillType]),
|
||||
@@ -217,6 +218,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::SelectiveColorChoice]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::GridType]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::ArcType]),
|
||||
// add here
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::MergeByDistanceAlgorithm]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::PointSpacingType]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::style::StrokeCap]),
|
||||
|
||||
Reference in New Issue
Block a user