mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-26 03:46:53 -06:00
Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1101dd7d0 | ||
|
|
47237bd4dd | ||
|
|
2521605713 | ||
|
|
f56be09993 | ||
|
|
1b6861698c | ||
|
|
f24af47ffb | ||
|
|
055b06a2f4 | ||
|
|
660a7881d1 | ||
|
|
3d8301b965 | ||
|
|
cb8b1709e9 | ||
|
|
66ecce06f5 | ||
|
|
004287d919 | ||
|
|
656dd4d09a |
Generated
+120
-120
@@ -4,9 +4,9 @@ version = 4
|
||||
|
||||
[[package]]
|
||||
name = "acadrust"
|
||||
version = "0.6.5"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5eaa72520f70eb6f7f09b06f44e1ed0d8fad5fa743364f2c3715c30df962ec7c"
|
||||
checksum = "8923c99ea1f151cb7fc17ce2fc14f8b1e330fa22611e535602d985c0cc3c89a2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.13.2",
|
||||
@@ -28,9 +28,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "accesskit"
|
||||
version = "0.25.1"
|
||||
version = "0.25.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad442f58ee04714aaa0ba0a2768c1ea1935b29507bb22ddece8cbbc76db02932"
|
||||
checksum = "4e883d1d28c5fd5568c79ed2d2bd48d9f2fd3945c8974ed0f53690ceddb051be"
|
||||
dependencies = [
|
||||
"enumn",
|
||||
"serde",
|
||||
@@ -39,9 +39,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_atspi_common"
|
||||
version = "0.21.0"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52c182f9c282ac9c5638d876d551d15e5f7d397ec263349a0c6a2b61595dd5e4"
|
||||
checksum = "24f5baf62e34a4a6c30e3d697e4782f821a7337049c44c3b40576a27dfbb580d"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_consumer",
|
||||
@@ -53,9 +53,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_consumer"
|
||||
version = "0.39.1"
|
||||
version = "0.39.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a45e2bb300bea6d5f33a82c3e051c3ddc6f41c209750f68a0acbe991a284344"
|
||||
checksum = "f3da8f27636015191b2264ee1df681503170a632cafd832fdc1edfdd4e3a2fd3"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"hashbrown 0.17.1",
|
||||
@@ -63,9 +63,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_ios"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28e539d64b27fd4c14de8f78ada8f7c944c092f52f7e49fb63316507a57f6937"
|
||||
checksum = "a5280724673bbfa8b30a838c013500b719392989be3b97c5a9964062c252c232"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_consumer",
|
||||
@@ -77,9 +77,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_macos"
|
||||
version = "0.27.1"
|
||||
version = "0.27.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5701624bf6a11efb6d4c8e922bf44126ed96250e5faa9a7a0468c979635839a4"
|
||||
checksum = "123828ceeec755b05255e3976cb0852993202de98a0626c97dd8d34246991f84"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_consumer",
|
||||
@@ -91,9 +91,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_unix"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "202f24df034a7476d07b7f74284de84f6d62aabd858dbe7ee9cad3b7ad6f8f9d"
|
||||
checksum = "30d5192871228c54f1b0fb4a85cef000e11a3804f25d2165f14afa0daa67f3f6"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_atspi_common",
|
||||
@@ -109,9 +109,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_windows"
|
||||
version = "0.35.1"
|
||||
version = "0.35.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce63f35d6bdcf59f26b76b3379063f738e6412cef46999cc772d46aa3de35adb"
|
||||
checksum = "498668b90da9995302a0055aaecad4756932333da2ddef6baee1c00844c430fc"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_consumer",
|
||||
@@ -123,9 +123,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_winit"
|
||||
version = "0.34.1"
|
||||
version = "0.34.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee96dd150375c05912d8a473f98c2fb155fb3234fac88292d396548a2460fa77"
|
||||
checksum = "cd4ab4437b520ff03088fbe48676a11e7d44a929535e64b31432c1073e48dbcc"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_ios",
|
||||
@@ -343,7 +343,7 @@ checksum = "4f8abc12baad266b1c8cec146854c195b5864b4221d4b2ca7296a7ae82d9e451"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -378,7 +378,7 @@ checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -444,9 +444,9 @@ checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
|
||||
|
||||
[[package]]
|
||||
name = "basic-udev"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a45f9771ced8a774de5e5ebffbe520f52e3943bf5a9a6baa3a5d14a5de1afe6"
|
||||
checksum = "868a8681ed3a19fae3e302c705b1cabf5be53278cc8720a9b82e9805c9ab171d"
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
@@ -541,7 +541,7 @@ checksum = "6a1f896587b6f2c069c73d2f0913e2d590c3990285cd2f0b6aa02b786b4c679c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -820,9 +820,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.6.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f74872d07caf508b30a21f6836e7d7016a2eaf7d9ff4f48deaa58cd8a0407630"
|
||||
checksum = "86e055fa6e3e40ad6187ba25b6855eeb635d5271f8e0f0e1e9a5826b8d75bea0"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
@@ -862,9 +862,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "5.4.1"
|
||||
version = "5.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
|
||||
checksum = "052af7878466feccda12ba7a6a67d019d712f296809559b47c12c8cc196d3a9a"
|
||||
dependencies = [
|
||||
"error-code",
|
||||
]
|
||||
@@ -907,9 +907,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "4.6.8"
|
||||
version = "4.6.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e"
|
||||
checksum = "4356eba6976435498d05a311ee9fc486da3bc81c200d3f0d7dd9703b41913f92"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"memchr",
|
||||
@@ -1044,9 +1044,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.2"
|
||||
version = "1.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01a7799fd6b852db0e61728dde9a204c423b44d689dbd432522543614b490e78"
|
||||
checksum = "0ba106e844045ddc02c20768ac4b0b7cb45f2cc2ecbd833ac9f5dacb183c2055"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -1162,7 +1162,7 @@ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1449,9 +1449,9 @@ source = "git+https://github.com/storytold/egui.git?rev=6d518fc799681571963842f9
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
@@ -1537,9 +1537,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.14"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484"
|
||||
checksum = "1c66915cc5391097de7a2b0b972e8505aa33c0df401fd3a6811dbdb32881ba48"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
@@ -1622,7 +1622,7 @@ checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1673,7 +1673,7 @@ checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2060,9 +2060,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "2.3.0"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513"
|
||||
checksum = "fe090a42a0d3140fed8c63263ed1fecc02422cb2f4bfe55cd8de2de1b8e105dd"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"potential_utf",
|
||||
@@ -2074,9 +2074,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icu_locale_core"
|
||||
version = "2.3.0"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb"
|
||||
checksum = "25d29b57c17c6528d0dc4c2141cfa2d08200465acb537a3909c8b9b8a4a984c0"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
@@ -2087,9 +2087,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "2.3.0"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f"
|
||||
checksum = "404a3b8c486f205c87fe7f23ab6894d84a4ad76d6e4671ae1981ba7def87c599"
|
||||
dependencies = [
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
@@ -2101,15 +2101,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "2.3.0"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0"
|
||||
checksum = "20f0088bfb7441f7a37fa0c9e20cb1e9cba1cac6b9ce8cd2a6dc44294f8e8cbc"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "2.3.0"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148"
|
||||
checksum = "2ea83d991c56158f7cf898bf86436c1dfe7c07ee82fe88951f2695c9f37a379f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
@@ -2122,15 +2122,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "2.3.0"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa"
|
||||
checksum = "b98f29af82d02b163fe50161388350e743bb3107cc8e8c2f7314cb505faddfcf"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "2.3.1"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73"
|
||||
checksum = "413099315636c588ee8f011190b329b971a09a8fae9d157e01dcae8379468514"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locale_core",
|
||||
@@ -2429,7 +2429,7 @@ dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"libc",
|
||||
"plain",
|
||||
"redox_syscall 0.9.4",
|
||||
"redox_syscall 0.9.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2458,9 +2458,9 @@ checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae"
|
||||
|
||||
[[package]]
|
||||
name = "litrs"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
|
||||
checksum = "4744e383959f0db86ede514b809b1c53251889093803c05267acc7d4e7030d70"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
@@ -2479,9 +2479,9 @@ checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
|
||||
|
||||
[[package]]
|
||||
name = "lyon_geom"
|
||||
version = "1.0.19"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4336502e29e32af93cf2dad2214ed6003c17ceb5bd499df77b1de663b9042b92"
|
||||
checksum = "d82dd26aa4dc0b5b70b9115de59824842bcb43cf4a28e1b1e6d0ad148c93f93e"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"euclid",
|
||||
@@ -2517,9 +2517,9 @@ checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.9.11"
|
||||
version = "0.9.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0"
|
||||
checksum = "6b0ca1155a9ecc25aa12e8313a119f32edf0255fea2f44839c4ac0a12b2ac157"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -3076,9 +3076,9 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "open"
|
||||
version = "5.4.4"
|
||||
version = "5.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa576c76302b7b808eecc68061e67336c47833ef9d22caa74dda10fa9675eebc"
|
||||
checksum = "8d8c8b8e725ad26404d6896eb2f064294a0a7b4f1d9120210d85544684c4ca95"
|
||||
dependencies = [
|
||||
"is-wsl",
|
||||
"libc",
|
||||
@@ -3102,9 +3102,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "5.5.0"
|
||||
version = "5.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c7c9e0d9b23589f26070720bac724174bfec1083e82f7854cdd0267518343c0"
|
||||
checksum = "7f59006ce1e204c30956638535b74d317bca99477b9356dfaec54c37f6f600ab"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
@@ -3580,9 +3580,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.9.4"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "737970939a87c6fa31e7acad13307bccbb017a073b695b6089a2c484f929e20e"
|
||||
checksum = "580c875845b41ecb1564ad1ae4703044cef774145e3d7a94dc2b3fa7c97868fd"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
]
|
||||
@@ -3894,7 +3894,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3918,14 +3918,14 @@ checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
|
||||
checksum = "4e7523beb55eece201a2356bee0bbca0d1ab466c14c07703b2e0ee6d42cb0c2c"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
@@ -4032,9 +4032,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.16.2"
|
||||
version = "1.16.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9395f0f0eee849a9b707b2f06bb92a6a422090e2123bb2ef8e87a0e61892a8e"
|
||||
checksum = "d0d5d4b1981b8bbbb7aed204cae5726d683d5d6ac7e74ec54122ea28f315e7c8"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -4180,9 +4180,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "3.0.6"
|
||||
version = "3.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee"
|
||||
checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4197,7 +4197,7 @@ checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4278,7 +4278,7 @@ checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4353,9 +4353,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643"
|
||||
checksum = "abc840c238154d58f9e86b2792261edfde565a17696e1cda1a449de27979801a"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
@@ -4369,18 +4369,18 @@ checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.53.2"
|
||||
version = "1.54.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e95f91fcc7a621e8b030f6aa23c71fe9838ae2fb4d8118b75602a328f5144044"
|
||||
checksum = "b52fd3255258d26fac7264ab5a76b53c13367136dc011d3d6ad4f8ed070396a3"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "1.1.6+spec-1.1.0"
|
||||
version = "1.1.8+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "920602543f0911ab71da12c50d59701da54c196d1a2bf5cb4b75667f137a406a"
|
||||
checksum = "20489e00e4d8741d6be680764cc12e270655e375a20d1011e844a9c3379e678d"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde_core",
|
||||
@@ -4393,18 +4393,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "1.1.1+spec-1.1.0"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
|
||||
checksum = "2b86d767906c6c42421dcba507eb9d203e779497710a47782a224bb871653053"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.25.15+spec-1.1.0"
|
||||
version = "0.25.17+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1340ea94a5856333492c9064b02c778b191dd2c853778d9609debdcdfea3a614"
|
||||
checksum = "e3641d5bbb5349a79e1020a242d251efbc546ad8048d133958323ce9c40a9c9c"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"toml_datetime",
|
||||
@@ -4414,18 +4414,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.1.3+spec-1.1.0"
|
||||
version = "1.1.5+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56"
|
||||
checksum = "baa693a8032d7e1cada7d0041e96126df243179ff061456783ac7f12bda4744c"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
version = "1.1.3+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
|
||||
checksum = "06bdbd8cfc056b8d2e2e85f29b56a3bdbecb527cef81eb39e3e7b98af4652770"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
@@ -4522,9 +4522,9 @@ checksum = "0b993bddc193ae5bd0d623b49ec06ac3e9312875fdae725a975c51db1cc1677f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.26"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
|
||||
checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
@@ -4614,9 +4614,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.27.0"
|
||||
version = "1.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97277d36b9c3ace13e58fa6e753f8b0bbbf302a18dd193240d70f9e29681059a"
|
||||
checksum = "4a0e2d11620c50695fe12bbbfde96b3477fe606e7284c22d505c12ce51214514"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde_core",
|
||||
@@ -4734,7 +4734,7 @@ dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -4904,9 +4904,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webbrowser"
|
||||
version = "1.2.4"
|
||||
version = "1.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62c35be770821a214dbc362fc26908c853e776c0004294d0b10b8a6bad582f94"
|
||||
checksum = "d2c8ddfbbf66e501434657400268cefe51b95140a0266236415cda7627732df7"
|
||||
dependencies = [
|
||||
"jni",
|
||||
"log",
|
||||
@@ -5533,9 +5533,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.8.3"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5"
|
||||
checksum = "417ef3fcf6c300b7dbd25c85531a746931c02ad536f3cf4786ed1e88223340b3"
|
||||
dependencies = [
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
@@ -5550,7 +5550,7 @@ checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -5622,7 +5622,7 @@ dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
"zbus_names",
|
||||
"zvariant",
|
||||
"zvariant_utils",
|
||||
@@ -5630,9 +5630,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zbus_names"
|
||||
version = "4.3.4"
|
||||
version = "4.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8bf88b4a3ff53e883001e0e0115b297a9d53c31b9c1edd2bfdd853e3428624e"
|
||||
checksum = "812a12be4ead7a59e18ea8f047d08add0066b73bc62fc1467066a2472ae088dd"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"winnow",
|
||||
@@ -5662,18 +5662,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.61"
|
||||
version = "0.8.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "879fb705ce98c32e41ebdb970fbe1204f8492423b314c6ab0354c3e7b5542866"
|
||||
checksum = "e5fe1f8f1b06191a00962174c61aa5005e0bb391a6d80d07e24d115c01a92ed8"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.61"
|
||||
version = "0.8.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "708882a28301d604fa039cc7727607a98d04c4b86dc76ec9cc683f805d709759"
|
||||
checksum = "863ad3ac83293fb4d740aedbfdc9240dd8d1a50c1099acd76ce80ce7c7230c7f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -5697,7 +5697,7 @@ checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -5720,9 +5720,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.11.8"
|
||||
version = "0.11.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8"
|
||||
checksum = "f58a2799cfe9a1670c7ea8edbd52ef8edb93cb37a7f83bf31ba9e35d9dae6a40"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
@@ -5731,20 +5731,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.11.6"
|
||||
version = "0.11.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da"
|
||||
checksum = "c3743ddbf47738d929cd1ae2e199d40d0162979dfe31aef8ae646d2bf00bf97a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "9.1.0"
|
||||
version = "9.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2886b00003a72c4ccee3f42cfc34908d33b2508a74d827dc2a4244706475c704"
|
||||
checksum = "518b847fd864440c379748ad684c449f293af68978a485a4ff16f7c901c1f7c4"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"flate2",
|
||||
@@ -5798,9 +5798,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "5.15.0"
|
||||
version = "5.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1d34c27cc6cdd1f458427519dd6b8612f7b7e3f7b9a0b2355d041dda9869147"
|
||||
checksum = "80bf6d6cd6a4b601acb64cfb8284cdc643fcfa75ef26ae3c46ec8aa4e8a1fbb7"
|
||||
dependencies = [
|
||||
"endi",
|
||||
"enumflags2",
|
||||
@@ -5813,14 +5813,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zvariant_derive"
|
||||
version = "5.15.0"
|
||||
version = "5.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "864155e69b4352db0c7f374917bf45d1e0c8d17659c8b3dbf9795f3673f8c497"
|
||||
checksum = "ef2672456ba1cb54b5eaec136e63135b3c7495fb938e53b28a62e633496d486d"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
"zvariant_utils",
|
||||
]
|
||||
|
||||
@@ -5833,7 +5833,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn 3.0.6",
|
||||
"syn 3.0.9",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ fn style(s: &Session) -> String {
|
||||
s.doc().map(|d| d.header.str("DIMSTYLE", "Standard")).unwrap_or_else(|_| "Standard".into())
|
||||
}
|
||||
|
||||
fn dim(s: &Session, kind: DimKind, defpt: Vec2, p13: Vec2, p14: Vec2, p15: Vec2, p16: Vec2, text: &str) -> EntityKind {
|
||||
fn dim(s: &Session, kind: DimKind, defpt: Vec2, p13: Vec2, p14: Vec2, p15: Vec2, text: &str) -> EntityKind {
|
||||
EntityKind::Dimension(Dimension {
|
||||
kind,
|
||||
defpt: v3(defpt),
|
||||
@@ -128,7 +128,7 @@ fn dim(s: &Session, kind: DimKind, defpt: Vec2, p13: Vec2, p14: Vec2, p15: Vec2,
|
||||
p13: v3(p13),
|
||||
p14: v3(p14),
|
||||
p15: v3(p15),
|
||||
p16: v3(p16),
|
||||
p16: v3(Vec2::ZERO),
|
||||
text: text.into(),
|
||||
style: style(s),
|
||||
measurement: 0.0,
|
||||
@@ -205,7 +205,10 @@ fn angular_from_lines(s: &Session, h1: Handle, h2: Handle, at: Vec2) -> Option<E
|
||||
// The picked leg end goes last in each pair, so the renderer measures between p14 and p15.
|
||||
let (o1, t1) = if s1 == AssocSnap::Start { (a2, AssocSnap::End) } else { (a1, AssocSnap::Start) };
|
||||
let (o2, t2) = if s2 == AssocSnap::Start { (b2, AssocSnap::End) } else { (b1, AssocSnap::Start) };
|
||||
let k = dim(s, DimKind::Angular, o2, o1, p1, p2, at, "");
|
||||
let mut k = dim(s, DimKind::Angular, o2, o1, p1, p2, "");
|
||||
if let EntityKind::Dimension(d) = &mut k {
|
||||
d.p16 = v3(at);
|
||||
}
|
||||
let mut assoc = Vec::new();
|
||||
if assoc_enabled(s) {
|
||||
assoc = vec![
|
||||
@@ -225,7 +228,7 @@ fn run_linear(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
};
|
||||
let at = point_req("dimlinear", p, "at")?;
|
||||
let rot = p.get("rotation").and_then(Value::as_f64).map(f64::to_radians);
|
||||
let k = dim(s, linear_kind(a, b, at, rot, false), at, a, b, Vec2::ZERO, Vec2::ZERO, str_param(p, "text").unwrap_or(""));
|
||||
let k = dim(s, linear_kind(a, b, at, rot, false), at, a, b, Vec2::ZERO, str_param(p, "text").unwrap_or(""));
|
||||
Ok(json!({ "handle": add_dim(s, k)?.hex() }))
|
||||
}
|
||||
|
||||
@@ -233,7 +236,7 @@ fn run_aligned(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let a = point_req("dimaligned", p, "p1")?;
|
||||
let b = point_req("dimaligned", p, "p2")?;
|
||||
let at = point_req("dimaligned", p, "at")?;
|
||||
let k = dim(s, DimKind::Aligned, at, a, b, Vec2::ZERO, Vec2::ZERO, str_param(p, "text").unwrap_or(""));
|
||||
let k = dim(s, DimKind::Aligned, at, a, b, Vec2::ZERO, str_param(p, "text").unwrap_or(""));
|
||||
Ok(json!({ "handle": add_dim(s, k)?.hex() }))
|
||||
}
|
||||
|
||||
@@ -261,9 +264,9 @@ fn radial(s: &mut Session, p: &Value, diameter: bool) -> Result<Value> {
|
||||
return Err(bad(id, "`point` must differ from `center`"));
|
||||
}
|
||||
let k = if diameter {
|
||||
dim(s, DimKind::Diameter, c + (c - pt), Vec2::ZERO, Vec2::ZERO, pt, Vec2::ZERO, "")
|
||||
dim(s, DimKind::Diameter, c + (c - pt), Vec2::ZERO, Vec2::ZERO, pt, "")
|
||||
} else {
|
||||
dim(s, DimKind::Radius, c, Vec2::ZERO, Vec2::ZERO, pt, Vec2::ZERO, "")
|
||||
dim(s, DimKind::Radius, c, Vec2::ZERO, Vec2::ZERO, pt, "")
|
||||
};
|
||||
let k = match at {
|
||||
Some(at) => text_at(k, at),
|
||||
@@ -308,7 +311,7 @@ fn run_angular(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
_ => return Err(bad("dimangular", "object is not an arc")),
|
||||
};
|
||||
let g = cadcraft_geom::Arc::new(a.center.xy(), a.radius, a.start, a.end);
|
||||
let k = dim(s, DimKind::Angular3P, at, g.start_point(), g.end_point(), g.center, Vec2::ZERO, "");
|
||||
let k = dim(s, DimKind::Angular3P, at, g.start_point(), g.end_point(), g.center, "");
|
||||
return Ok(json!({ "handle": add_dim(s, k)?.hex() }));
|
||||
}
|
||||
let v = point_req("dimangular", p, "vertex")?;
|
||||
@@ -318,7 +321,7 @@ fn run_angular(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
if v.near(a, 1e-12) || v.near(b, 1e-12) {
|
||||
return Err(bad("dimangular", "`p1` and `p2` must differ from `vertex`"));
|
||||
}
|
||||
let k = dim(s, DimKind::Angular3P, at, a, b, v, Vec2::ZERO, "");
|
||||
let k = dim(s, DimKind::Angular3P, at, a, b, v, "");
|
||||
Ok(json!({ "handle": add_dim(s, k)?.hex() }))
|
||||
}
|
||||
|
||||
@@ -330,7 +333,7 @@ fn run_arclen(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
_ => return Err(bad("dimarc", "object is not an arc")),
|
||||
};
|
||||
let ga = cadcraft_geom::Arc::new(a.center.xy(), a.radius, a.start, a.end);
|
||||
let k = dim(s, DimKind::ArcLength, at, ga.start_point(), ga.end_point(), ga.center, Vec2::ZERO, "");
|
||||
let k = dim(s, DimKind::ArcLength, at, ga.start_point(), ga.end_point(), ga.center, "");
|
||||
Ok(json!({ "handle": add_dim(s, k)?.hex() }))
|
||||
}
|
||||
|
||||
@@ -338,7 +341,7 @@ fn run_ordinate(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let f = point_req("dimordinate", p, "feature")?;
|
||||
let l = point_req("dimordinate", p, "leader")?;
|
||||
let xtype = p.get("xtype").and_then(Value::as_bool).unwrap_or((l - f).y.abs() > (l - f).x.abs());
|
||||
let k = dim(s, DimKind::Ordinate { x_type: xtype }, Vec2::ZERO, f, l, Vec2::ZERO, Vec2::ZERO, "");
|
||||
let k = dim(s, DimKind::Ordinate { x_type: xtype }, Vec2::ZERO, f, l, Vec2::ZERO, "");
|
||||
Ok(json!({ "handle": add_dim(s, k)?.hex() }))
|
||||
}
|
||||
|
||||
@@ -1060,7 +1063,7 @@ impl Interactive for LinearM {
|
||||
}
|
||||
(_, Input::Point(at)) => {
|
||||
let (Some(&a), Some(&b)) = (self.pts.first(), self.pts.get(1)) else { return Ok(Step::Continue) };
|
||||
let k = self.text.apply(dim(s, linear_kind(a, b, at, self.rotation, self.aligned), at, a, b, Vec2::ZERO, Vec2::ZERO, ""));
|
||||
let k = self.text.apply(dim(s, linear_kind(a, b, at, self.rotation, self.aligned), at, a, b, Vec2::ZERO, ""));
|
||||
add_dim(s, k)?;
|
||||
if let Some(EntityKind::Dimension(d)) = s.last_dim.and_then(|h| s.doc().ok()?.entity(h).map(|e| e.kind.clone())) {
|
||||
let st = s.doc()?.dim_style(&d.style).cloned().unwrap_or_default();
|
||||
@@ -1087,7 +1090,7 @@ impl Interactive for LinearM {
|
||||
fn preview(&self, s: &Session, c: Vec2) -> Vec<EntityKind> {
|
||||
match self.pts.as_slice() {
|
||||
[a] => vec![super::helpers::line(*a, c)],
|
||||
[a, b] => vec![self.text.apply(dim(s, linear_kind(*a, *b, c, self.rotation, self.aligned), c, *a, *b, Vec2::ZERO, Vec2::ZERO, ""))],
|
||||
[a, b] => vec![self.text.apply(dim(s, linear_kind(*a, *b, c, self.rotation, self.aligned), c, *a, *b, Vec2::ZERO, ""))],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
@@ -1106,9 +1109,9 @@ impl RadialM {
|
||||
fn kind(&self, s: &Session, c: Vec2, r: f64, at: Vec2) -> EntityKind {
|
||||
let pt = c + (at - c).normalized() * r;
|
||||
let k = self.text.apply(if self.diameter {
|
||||
dim(s, DimKind::Diameter, c + (c - pt), Vec2::ZERO, Vec2::ZERO, pt, Vec2::ZERO, "")
|
||||
dim(s, DimKind::Diameter, c + (c - pt), Vec2::ZERO, Vec2::ZERO, pt, "")
|
||||
} else {
|
||||
dim(s, DimKind::Radius, c, Vec2::ZERO, Vec2::ZERO, pt, Vec2::ZERO, "")
|
||||
dim(s, DimKind::Radius, c, Vec2::ZERO, Vec2::ZERO, pt, "")
|
||||
});
|
||||
text_at(k, at)
|
||||
}
|
||||
@@ -1241,7 +1244,7 @@ impl Interactive for AngularM {
|
||||
([h1, h2], None) => angular_from_lines(s, *h1, *h2, at),
|
||||
_ => None,
|
||||
};
|
||||
let k = k.unwrap_or_else(|| dim(s, DimKind::Angular3P, at, a, b, v, Vec2::ZERO, ""));
|
||||
let k = k.unwrap_or_else(|| dim(s, DimKind::Angular3P, at, a, b, v, ""));
|
||||
add_dim(s, self.text.apply(k))?;
|
||||
Ok(Step::Done)
|
||||
}
|
||||
@@ -1307,9 +1310,7 @@ impl Interactive for AngularM {
|
||||
}
|
||||
}
|
||||
fn preview(&self, s: &Session, c: Vec2) -> Vec<EntityKind> {
|
||||
self.geometry()
|
||||
.map(|(v, a, b)| vec![self.text.apply(dim(s, DimKind::Angular3P, self.located(v, c), a, b, v, Vec2::ZERO, ""))])
|
||||
.unwrap_or_default()
|
||||
self.geometry().map(|(v, a, b)| vec![self.text.apply(dim(s, DimKind::Angular3P, self.located(v, c), a, b, v, ""))]).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1385,7 +1386,7 @@ impl Interactive for ArcLenM {
|
||||
Ok(Step::Continue)
|
||||
}
|
||||
(Some(g), Input::Point(at)) => {
|
||||
add_dim(s, self.text.apply(dim(s, DimKind::ArcLength, at, g.start_point(), g.end_point(), g.center, Vec2::ZERO, "")))?;
|
||||
add_dim(s, self.text.apply(dim(s, DimKind::ArcLength, at, g.start_point(), g.end_point(), g.center, "")))?;
|
||||
Ok(Step::Done)
|
||||
}
|
||||
(Some(_), Input::Keyword(k)) => {
|
||||
@@ -1404,9 +1405,7 @@ impl Interactive for ArcLenM {
|
||||
if self.partial.is_some() {
|
||||
return Vec::new();
|
||||
}
|
||||
self.arc
|
||||
.map(|g| vec![self.text.apply(dim(s, DimKind::ArcLength, c, g.start_point(), g.end_point(), g.center, Vec2::ZERO, ""))])
|
||||
.unwrap_or_default()
|
||||
self.arc.map(|g| vec![self.text.apply(dim(s, DimKind::ArcLength, c, g.start_point(), g.end_point(), g.center, ""))]).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1421,7 +1420,7 @@ struct OrdinateM {
|
||||
impl OrdinateM {
|
||||
fn kind(&self, s: &Session, f: Vec2, l: Vec2) -> EntityKind {
|
||||
let x = self.xtype.unwrap_or((l - f).y.abs() > (l - f).x.abs());
|
||||
self.text.apply(dim(s, DimKind::Ordinate { x_type: x }, Vec2::ZERO, f, l, Vec2::ZERO, Vec2::ZERO, ""))
|
||||
self.text.apply(dim(s, DimKind::Ordinate { x_type: x }, Vec2::ZERO, f, l, Vec2::ZERO, ""))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -283,7 +283,7 @@ fn jogged_kind(s: &Session, c: Vec2, r: f64, center: Vec2, at: Vec2, jog: Option
|
||||
}
|
||||
let tip = c + dir * r;
|
||||
let jog = jog.unwrap_or_else(|| center.mid(tip));
|
||||
let mut k = dim(s, DimKind::Radius, c, Vec2::ZERO, Vec2::ZERO, tip, Vec2::ZERO, text);
|
||||
let mut k = dim(s, DimKind::Radius, c, Vec2::ZERO, Vec2::ZERO, tip, text);
|
||||
if let EntityKind::Dimension(dm) = &mut k {
|
||||
dm.marks.jogged = Some(JoggedRadius { center: v3(center), jog: v3(jog) });
|
||||
if at.dist(c) > r + 1e-9 {
|
||||
|
||||
@@ -96,9 +96,9 @@ fn plan(
|
||||
let dir = if dir == Vec2::ZERO || !dir.is_finite() { Vec2::from_angle(std::f64::consts::FRAC_PI_4) } else { dir };
|
||||
let p = *c + dir * *r;
|
||||
out.push(if mode == Mode::Diameter {
|
||||
dim(s, DimKind::Diameter, *c - dir * *r, Vec2::ZERO, Vec2::ZERO, p, Vec2::ZERO, "")
|
||||
dim(s, DimKind::Diameter, *c - dir * *r, Vec2::ZERO, Vec2::ZERO, p, "")
|
||||
} else {
|
||||
dim(s, DimKind::Radius, *c, Vec2::ZERO, Vec2::ZERO, p, Vec2::ZERO, "")
|
||||
dim(s, DimKind::Radius, *c, Vec2::ZERO, Vec2::ZERO, p, "")
|
||||
});
|
||||
}
|
||||
if out.is_empty() {
|
||||
@@ -131,7 +131,7 @@ fn plan(
|
||||
.iter()
|
||||
.map(|p| {
|
||||
let end = if vertical { Vec2::new(at.x, p.y) } else { Vec2::new(p.x, at.y) };
|
||||
dim(s, DimKind::Ordinate { x_type: !vertical }, origin, *p, end, Vec2::ZERO, Vec2::ZERO, "")
|
||||
dim(s, DimKind::Ordinate { x_type: !vertical }, origin, *p, end, Vec2::ZERO, "")
|
||||
})
|
||||
.collect());
|
||||
}
|
||||
@@ -147,8 +147,7 @@ fn plan(
|
||||
let mid = bounds.center();
|
||||
let out_dir =
|
||||
if vertical { Vec2::new(if at.x >= mid.x { 1.0 } else { -1.0 }, 0.0) } else { Vec2::new(0.0, if at.y >= mid.y { 1.0 } else { -1.0 }) };
|
||||
let lin =
|
||||
|a: Vec2, b: Vec2, k: usize| dim(s, DimKind::Linear { rotation: rot }, at + out_dir * (step * k as f64), a, b, Vec2::ZERO, Vec2::ZERO, "");
|
||||
let lin = |a: Vec2, b: Vec2, k: usize| dim(s, DimKind::Linear { rotation: rot }, at + out_dir * (step * k as f64), a, b, Vec2::ZERO, "");
|
||||
Ok(match mode {
|
||||
Mode::Baseline => {
|
||||
// From the datum (the point nearest it, by default the first) to every other point.
|
||||
|
||||
@@ -16,7 +16,8 @@ use serde_json::{Value, json};
|
||||
|
||||
use super::super::helpers::v3;
|
||||
use super::super::machines::{SelOutcome, SelectPhase, number};
|
||||
use super::super::*;
|
||||
use super::super::{CommandSpec, bad, bool_or, point_param, targets};
|
||||
|
||||
use super::{KEYWORDS, angle, check_size, count, fmt, nesting, other, yes, yes_no};
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@ use std::sync::Arc;
|
||||
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::*;
|
||||
use super::{CommandSpec, bad, bool_or, file, str_param};
|
||||
|
||||
use super::always;
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
|
||||
@@ -12,7 +12,9 @@ use serde_json::{Value, json};
|
||||
|
||||
use super::helpers::v3;
|
||||
use super::machines::{SelOutcome, SelectPhase, number};
|
||||
use super::*;
|
||||
use super::{CommandSpec, bad, bool_or, f64_or, find_command, ok, point_param, point_req, point_value, str_param, targets};
|
||||
|
||||
use super::has_doc;
|
||||
use crate::{Accept, EngineError, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
mod attedit;
|
||||
|
||||
@@ -14,7 +14,8 @@ use cadcraft_geom::{Line, Vec2};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::machines::{SelOutcome, SelectPhase};
|
||||
use super::*;
|
||||
use super::{CommandSpec, bad, bool_or, f64_or, point_value, str_param, targets};
|
||||
|
||||
use crate::{Accept, EngineError, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
macro_rules! gc {
|
||||
|
||||
@@ -5,10 +5,15 @@ use cadcraft_doc::{EntityKind, HAlign, MText, Point, RayLine, Text, VAlign};
|
||||
use cadcraft_geom::{Arc, Circle, Ellipse, PolyVertex, Polyline, Spline, Vec2, arc_to_bulge, bulge_to_arc};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::helpers::*;
|
||||
use super::helpers::{arc, circle, line, lwpoly, polygon_from_edge, polygon_typed_radius_point, polygon_vertices, v3};
|
||||
|
||||
use super::machines::number;
|
||||
use super::pline_opts::{Outcome, PlineAsk};
|
||||
use super::*;
|
||||
use super::{
|
||||
CommandSpec, bad, bool_or, distinct_points, f64_or, f64_req, find_command, point_param, point_req, point_value, points_param, size_param,
|
||||
str_param,
|
||||
};
|
||||
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step, snap};
|
||||
|
||||
/// Every draw command's spec: JSON parameters, menu path, aliases and the interactive prompt
|
||||
|
||||
@@ -7,9 +7,11 @@ use cadcraft_geom::{Arc, Circle, Ellipse, Line, PolyVertex, Polyline, Spline, TA
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::curves::{self, Chain, MAX_GEN, TanObj};
|
||||
use super::helpers::*;
|
||||
use super::helpers::{arc, circle, line, lwpoly, rect_vertices, rect_with_corners, v3};
|
||||
|
||||
use super::machines::number;
|
||||
use super::*;
|
||||
use super::{CommandSpec, bad, bool_or, f64_or, f64_req, point_param, point_req, point_value, points_param, size_param, str_param, targets};
|
||||
|
||||
use crate::{Accept, EngineError, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
pub(crate) mod breakline;
|
||||
@@ -1572,7 +1574,7 @@ impl Interactive for XlineM2 {
|
||||
// RECTANG with Area / Dimensions / Rotation / Width / Elevation / Thickness
|
||||
// =====================================================================================
|
||||
|
||||
fn rect_kind(first: Vec2, w: f64, h: f64, rot: f64, fillet: f64, chamfer: (f64, f64), width: f64, elevation: f64) -> EntityKind {
|
||||
fn rect_kind(first: Vec2, (w, h): (f64, f64), rot: f64, fillet: f64, chamfer: (f64, f64), width: f64, elevation: f64) -> EntityKind {
|
||||
let local = rect_with_corners(Vec2::ZERO, Vec2::new(w, h), fillet, chamfer);
|
||||
let vs = local.into_iter().map(|v| PolyVertex { p: first + v.p.rotate(rot), ..v }).collect();
|
||||
let mut k = lwpoly(vs, true);
|
||||
@@ -1631,7 +1633,7 @@ pub(crate) fn run_rectang2(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
return Err(bad("rectang", "rectangle has no area"));
|
||||
}
|
||||
let first = if centered { a - Vec2::new(w / 2.0, h / 2.0).rotate(rot) } else { a };
|
||||
let k = rect_kind(first, w, h, rot, fillet, ch, width, f64_or(p, "elevation", 0.0));
|
||||
let k = rect_kind(first, (w, h), rot, fillet, ch, width, f64_or(p, "elevation", 0.0));
|
||||
let hd = s.add_entity(k)?;
|
||||
// A rectangle has its own thickness option and does not take THICKNESS.
|
||||
let th = f64_or(p, "thickness", 0.0);
|
||||
@@ -1683,7 +1685,8 @@ impl RectM2 {
|
||||
return Err(other("The rectangle has no area."));
|
||||
}
|
||||
let first = self.origin(w, h).unwrap_or(a);
|
||||
let hd = s.add_entity(rect_kind(first, w, h, self.rotation, self.fillet, self.chamfer, self.width, self.elevation))?;
|
||||
let hd = s.add_entity(rect_kind(first, (w, h), self.rotation, self.fillet, self.chamfer, self.width, self.elevation))?;
|
||||
// A rectangle has its own thickness option and does not take THICKNESS.
|
||||
let t = self.thickness;
|
||||
s.doc_mut()?.modify_entity(hd, |e| e.common.thickness = t)?;
|
||||
Ok(Step::Done)
|
||||
@@ -1861,7 +1864,7 @@ impl Interactive for RectM2 {
|
||||
}
|
||||
match self.other_corner(c).and_then(|(w, h)| self.origin(w, h).map(|a| (a, w, h))) {
|
||||
Some((a, w, h)) if w.abs() > 1e-12 && h.abs() > 1e-12 => {
|
||||
vec![rect_kind(a, w, h, self.rotation, self.fillet, self.chamfer, self.width, self.elevation)]
|
||||
vec![rect_kind(a, (w, h), self.rotation, self.fillet, self.chamfer, self.width, self.elevation)]
|
||||
}
|
||||
_ => Vec::new(),
|
||||
}
|
||||
|
||||
@@ -9,10 +9,11 @@ use cadcraft_doc::{Drawing, EntityKind};
|
||||
use cadcraft_geom::{PolyVertex, Vec2};
|
||||
use serde_json::Value;
|
||||
|
||||
use super::super::machines::number;
|
||||
use super::added;
|
||||
use super::curves::MAX_GEN;
|
||||
use super::machines::number;
|
||||
use super::*;
|
||||
use super::{CommandSpec, line, lwpoly, point_param, point_req, size_param, str_param};
|
||||
|
||||
use crate::cmd::modify2::as_lwpoly;
|
||||
use crate::{Accept, EngineError, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
|
||||
@@ -9,9 +9,11 @@ use cadcraft_doc::{
|
||||
use cadcraft_geom::{Line, Vec2, line_line_infinite};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::super::machines::number;
|
||||
use super::super::{distinct_points, ok};
|
||||
use super::curves::{self, MAX_GEN};
|
||||
use super::machines::number;
|
||||
use super::*;
|
||||
use super::{CommandSpec, bad, bool_or, f64_or, point_req, points_param, str_param};
|
||||
|
||||
use crate::{Accept, EngineError, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
|
||||
@@ -8,7 +8,8 @@ use cadcraft_doc::{EntityKind, Handle};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::machines::{SelOutcome, SelectPhase, SelectRun};
|
||||
use super::*;
|
||||
use super::{CommandSpec, EngineError, bad, ok, targets};
|
||||
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
|
||||
@@ -5,7 +5,8 @@ use cadcraft_geom::{Bounds2, Mat3, Vec2};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::machines::{SelOutcome, SelectPhase, SelectRun};
|
||||
use super::*;
|
||||
use super::{CommandSpec, EngineError, bad, bool_or, clipboard, f64_or, has_selection, point_param, point_req, points_param, str_param, targets};
|
||||
|
||||
use crate::undoctl::Control;
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ use cadcraft_render::raster::{RasterOptions, View};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::file::{base64_decode, base64_encode, io};
|
||||
use super::*;
|
||||
use super::{CommandSpec, bad, point_value, str_param};
|
||||
|
||||
use crate::{EngineError, Result, Session};
|
||||
|
||||
/// Formats EXPORT writes (the io layer picks the writer from the extension).
|
||||
@@ -557,7 +558,10 @@ mod tests {
|
||||
use cadcraft_geom::Vec3;
|
||||
|
||||
use super::super::file::{IoHooks, set_io};
|
||||
|
||||
use super::super::resolve_alias;
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
static DELIVERED: std::sync::Mutex<Vec<(String, String, usize)>> = std::sync::Mutex::new(Vec::new());
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ use cadcraft_doc::Drawing;
|
||||
pub use cadcraft_dxf::Version;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::*;
|
||||
use super::{CommandSpec, EngineError, Interactive, always, bad, bool_or, find_command, ok, str_param};
|
||||
|
||||
use crate::{Accept, Input, Prompt, Result, Session, Step};
|
||||
|
||||
/// The rules the Command Aliases dialog checks before it sends an alias to ALIASEDIT.
|
||||
@@ -731,6 +732,7 @@ mod tests {
|
||||
#[cfg(test)]
|
||||
mod pr193_tests {
|
||||
use super::*;
|
||||
use crate::Session;
|
||||
|
||||
#[test]
|
||||
fn failed_store_keeps_unsaved_changes_and_delivered_save_clears_them() {
|
||||
|
||||
@@ -12,7 +12,8 @@ use cadcraft_doc::{Drawing, Entity, EntityKind, Handle, MAX_BLOCK_DEPTH, Space,
|
||||
use cadcraft_geom::{Bounds2, Vec2};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::super::*;
|
||||
use super::super::{CommandSpec, bad, str_param};
|
||||
|
||||
use super::{base64_decode, io};
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
|
||||
@@ -12,8 +12,9 @@ use std::sync::{Mutex, MutexGuard, OnceLock, PoisonError};
|
||||
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::super::always;
|
||||
use super::super::helpers::support_text;
|
||||
use super::*;
|
||||
use super::{CommandSpec, Result, Session, bad, bool_or, find_command};
|
||||
|
||||
/// Most user aliases held.
|
||||
pub const MAX_ALIASES: usize = 10_000;
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::sync::{Mutex, MutexGuard, OnceLock, PoisonError};
|
||||
use cadcraft_doc::Drawing;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::*;
|
||||
use super::{Result, Session, bad, io};
|
||||
|
||||
/// The default template (profile setting, process-wide like FONTALT): empty = none.
|
||||
fn default_template() -> MutexGuard<'static, String> {
|
||||
|
||||
@@ -5,7 +5,8 @@ use cadcraft_geom::{Mat3, Vec2};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::curves;
|
||||
use super::*;
|
||||
use super::{CommandSpec, bad, bool_or, point_param, point_req, str_param, targets};
|
||||
|
||||
use crate::grips::{GripAction, GripMode, grip_action, mode_matrix, stretch_grip};
|
||||
use crate::{EngineError, Result, Session};
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ use cadcraft_doc::{Drawing, Group, Handle};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::machines::{SelOutcome, SelectPhase};
|
||||
use super::*;
|
||||
use super::{CommandSpec, bad, bool_or, str_param, targets};
|
||||
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
mod dash;
|
||||
|
||||
@@ -7,7 +7,8 @@ use serde_json::{Value, json};
|
||||
|
||||
use super::super::machines::{SelOutcome, SelectPhase, number};
|
||||
use super::super::qselect::wildcard;
|
||||
use super::super::*;
|
||||
use super::super::{CommandSpec, bad, bool_or, str_param, targets};
|
||||
|
||||
use super::{add_members, create, echo_list, find, remove_members, rename, ungroup, valid_name};
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
|
||||
@@ -7,7 +7,9 @@ use std::collections::{HashMap, HashSet};
|
||||
use cadcraft_doc::Handle;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::super::*;
|
||||
use super::super::{CommandSpec, bad};
|
||||
|
||||
use super::super::always;
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
|
||||
@@ -11,11 +11,13 @@ use serde_json::{Value, json};
|
||||
|
||||
use super::helpers::lwpoly;
|
||||
use super::*;
|
||||
|
||||
use crate::{Accept, EngineError, Input, Interactive, Picker, Prompt, Result, Session, Step};
|
||||
|
||||
mod assoc;
|
||||
mod edit;
|
||||
mod pick;
|
||||
use super::has_doc;
|
||||
pub(crate) use assoc::update as refill_associative;
|
||||
pub use pick::{HatchPicks, PickMode};
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ use cadcraft_doc::library::{PatternLine, pattern, standard_patterns};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::super::helpers::support_text;
|
||||
use super::*;
|
||||
use super::{Result, Session, bad, bool_or};
|
||||
|
||||
/// Most user patterns held at once.
|
||||
pub const MAX_USER_PATTERNS: usize = 4096;
|
||||
|
||||
@@ -6,7 +6,8 @@ use cadcraft_doc::{Entity, EntityKind, Handle};
|
||||
use cadcraft_geom::{Polyline, Vec2, Vec3};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::*;
|
||||
use super::{CommandSpec, bad, bool_or, f64_req, point_req, point_value, points_param, targets};
|
||||
|
||||
use crate::units::{format_angle, format_distance};
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
@@ -1130,6 +1131,8 @@ impl Interactive for AreaM {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use cadcraft_doc::Handle;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// #1440 review: a value one ulp past its bound (a border rectangle's shoelace area against
|
||||
|
||||
@@ -9,7 +9,8 @@ use cadcraft_doc::{Drawing, Entity, EntityKind, Handle, Insert, MAX_BLOCK_DEPTH,
|
||||
use cadcraft_geom::{Mat3, Vec2};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::super::*;
|
||||
use super::super::{bad, bool_or, point_req, str_param, targets};
|
||||
|
||||
use super::SelfCheck;
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@ use cadcraft_doc::{EntityKind, Handle};
|
||||
use cadcraft_geom::{Arc as GeoArc, EPS, Vec2};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::super::*;
|
||||
use super::super::bad;
|
||||
|
||||
use crate::{Result, Session};
|
||||
|
||||
const CMD: &str = "loops";
|
||||
|
||||
@@ -11,7 +11,8 @@ use cadcraft_geom::{Line, Vec2, line_line, point_in_polygon};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::super::machines::{SelOutcome, SelectPhase, SelectRun};
|
||||
use super::super::*;
|
||||
use super::super::{CommandSpec, bad, targets};
|
||||
|
||||
use super::SelfCheck;
|
||||
use crate::{EngineError, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@ use cadcraft_doc::{Entity, EntityKind};
|
||||
use cadcraft_geom::{Bounds2, Polyline, Vec2, intersect_segments};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::super::*;
|
||||
use super::super::bad;
|
||||
|
||||
use super::super::point_value;
|
||||
use super::{SelfCheck, area_of, object_area};
|
||||
use crate::{Result, Session};
|
||||
|
||||
|
||||
@@ -8,8 +8,9 @@ use std::collections::{HashMap, HashSet};
|
||||
use cadcraft_doc::{Drawing, Entity, EntityKind};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::super::bad;
|
||||
use super::super::blocks::purge;
|
||||
use super::super::*;
|
||||
|
||||
use crate::{Result, Session};
|
||||
|
||||
const CMD: &str = "usage";
|
||||
@@ -280,11 +281,9 @@ impl Unused {
|
||||
fn item_json(
|
||||
c: Category,
|
||||
name: &str,
|
||||
entities: usize,
|
||||
block_entities: usize,
|
||||
(entities, block_entities): (usize, usize),
|
||||
references: usize,
|
||||
used: bool,
|
||||
empty: bool,
|
||||
(used, empty): (bool, bool),
|
||||
current: Option<bool>,
|
||||
unused: &Unused,
|
||||
) -> Value {
|
||||
@@ -314,13 +313,13 @@ fn items(scan: &Scan, unused: &Unused, d: &Drawing) -> Vec<Value> {
|
||||
let (e, b) = scan.layers.get(&l.name.to_ascii_lowercase()).copied().unwrap_or_default();
|
||||
let uses = e.saturating_add(b);
|
||||
let cur = l.name.eq_ignore_ascii_case(¤t_layer);
|
||||
out.push(item_json(Category::Layer, &l.name, e, b, 0, uses > 0, uses == 0, Some(cur), unused));
|
||||
out.push(item_json(Category::Layer, &l.name, (e, b), 0, (uses > 0, uses == 0), Some(cur), unused));
|
||||
}
|
||||
for b in d.blocks.values() {
|
||||
let (e, n) = scan.blocks.get(&b.name.to_ascii_lowercase()).copied().unwrap_or_default();
|
||||
// A definition is used when a reference to it is reachable, as PURGE counts it.
|
||||
let used = scan.used_blocks.contains(&b.name.to_ascii_uppercase());
|
||||
out.push(item_json(Category::Block, &b.name, e, n, 0, used, e.saturating_add(n) == 0, None, unused));
|
||||
out.push(item_json(Category::Block, &b.name, (e, n), 0, (used, e.saturating_add(n) == 0), None, unused));
|
||||
}
|
||||
let current_style = d.header.str("TEXTSTYLE", "Standard");
|
||||
for t in &d.text_styles {
|
||||
@@ -328,7 +327,7 @@ fn items(scan: &Scan, unused: &Unused, d: &Drawing) -> Vec<Value> {
|
||||
let r = scan.style_refs.get(&t.name.to_ascii_lowercase()).copied().unwrap_or(0);
|
||||
let uses = e.saturating_add(b).saturating_add(r);
|
||||
let cur = t.name.eq_ignore_ascii_case(¤t_style);
|
||||
out.push(item_json(Category::TextStyle, &t.name, e, b, r, uses > 0, uses == 0, Some(cur), unused));
|
||||
out.push(item_json(Category::TextStyle, &t.name, (e, b), r, (uses > 0, uses == 0), Some(cur), unused));
|
||||
}
|
||||
let current_linetype = d.header.str("CELTYPE", "ByLayer");
|
||||
for t in &d.linetypes {
|
||||
@@ -336,7 +335,7 @@ fn items(scan: &Scan, unused: &Unused, d: &Drawing) -> Vec<Value> {
|
||||
let r = scan.linetype_refs.get(&t.name.to_ascii_lowercase()).copied().unwrap_or(0);
|
||||
let uses = e.saturating_add(b).saturating_add(r);
|
||||
let cur = t.name.eq_ignore_ascii_case(¤t_linetype);
|
||||
out.push(item_json(Category::Linetype, &t.name, e, b, r, uses > 0, uses == 0, Some(cur), unused));
|
||||
out.push(item_json(Category::Linetype, &t.name, (e, b), r, (uses > 0, uses == 0), Some(cur), unused));
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ use cadcraft_doc::{Layer, Lineweight};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::machines::SelectRun;
|
||||
use super::*;
|
||||
use super::{CommandSpec, bad, bool_or, ok, str_param, targets};
|
||||
|
||||
use crate::{Result, Session};
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
|
||||
@@ -10,6 +10,7 @@ use serde_json::{Value, json};
|
||||
|
||||
use super::file::{base64_encode, io};
|
||||
use super::*;
|
||||
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
use viewports::MviewM;
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
use cadcraft_doc::{Drawing, PlotStyle, PlotStyleKind, PlotStyleTable};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::*;
|
||||
use super::super::targets;
|
||||
use super::{CommandSpec, Result, Session, bad, str_param};
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
vec![
|
||||
|
||||
@@ -9,7 +9,8 @@ use serde_json::{Value, json};
|
||||
|
||||
use super::helpers::v3;
|
||||
use super::machines::{SelOutcome, SelectPhase};
|
||||
use super::*;
|
||||
use super::{CommandSpec, bad, point_req, points_param, str_param, targets};
|
||||
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
/// Most arrowheads added by one call, and most multileaders aligned / collected by one call.
|
||||
|
||||
@@ -7,10 +7,12 @@ use cadcraft_geom::{
|
||||
};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::helpers::*;
|
||||
use super::helpers::{arc, circle, line, lwpoly, v3};
|
||||
|
||||
use super::machines::{SelOutcome, SelectPhase, number};
|
||||
use super::trimextend::{self, TrimM};
|
||||
use super::*;
|
||||
use super::{CommandSpec, bad, bool_or, f64_or, f64_req, point_param, point_req, points_param, str_param, targets};
|
||||
|
||||
use crate::select::{SelectKind, SelectMode};
|
||||
use crate::{Accept, EngineError, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
|
||||
@@ -9,9 +9,11 @@ use cadcraft_geom::{
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::curves::{self, Chain, MAX_GEN, TanObj};
|
||||
use super::helpers::*;
|
||||
use super::helpers::{arc, line, lwpoly, v3};
|
||||
|
||||
use super::machines::{SelOutcome, SelectPhase, number};
|
||||
use super::*;
|
||||
use super::{CommandSpec, bad, bool_or, f64_or, f64_req, point_param, point_req, str_param, targets};
|
||||
|
||||
use crate::{Accept, EngineError, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
mod curve_edits;
|
||||
|
||||
@@ -80,8 +80,7 @@ fn same_polyline(
|
||||
s: &mut Session,
|
||||
h: Handle,
|
||||
pl: &LwPolyline,
|
||||
p1: Vec2,
|
||||
p2: Vec2,
|
||||
(p1, p2): (Vec2, Vec2),
|
||||
r: f64,
|
||||
chamfer: Option<ChamferDistances>,
|
||||
trim: bool,
|
||||
@@ -223,7 +222,7 @@ pub(crate) fn fillet_pline(
|
||||
_ => None,
|
||||
};
|
||||
let made = match (&e1.kind, &e2.kind) {
|
||||
(EntityKind::LwPolyline(pl), _) if h1 == h2 => same_polyline(s, h1, pl, p1, p2, r, chamfer, trim)?,
|
||||
(EntityKind::LwPolyline(pl), _) if h1 == h2 => same_polyline(s, h1, pl, (p1, p2), r, chamfer, trim)?,
|
||||
(EntityKind::LwPolyline(pl), k) if as_line(k).is_some() => {
|
||||
let ln = as_line(k).ok_or_else(|| other("Select a line."))?;
|
||||
polyline_and_line(s, (h1, pl, p1), (h2, ln, p2), true, r, chamfer, trim)?
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
//! PEDIT Edit vertex: Break and Straighten between two marked vertices, Tangent directions
|
||||
//! that Fit honours, and Regen.
|
||||
|
||||
use super::*;
|
||||
use super::super::point_value;
|
||||
use super::{
|
||||
EntityKind, Handle, Input, LwPolyline, PPhase, PeditM, PolyVertex, Result, Session, Value, Vec2, bad, curves, json, other, push_entity, set_kind,
|
||||
};
|
||||
|
||||
fn ordered(pl: &LwPolyline, i: usize, j: usize) -> Result<(usize, usize)> {
|
||||
let n = pl.vertices.len();
|
||||
|
||||
@@ -6,7 +6,11 @@
|
||||
//! parametrisation, end tangents and fit tolerance. Control-point edits change the curve
|
||||
//! directly and drop the fit data, as the curve no longer follows it.
|
||||
|
||||
use super::*;
|
||||
use super::super::{point_value, points_param};
|
||||
use super::{
|
||||
Accept, Entity, EntityKind, Handle, Input, Interactive, LwPolyline, MAX_GEN, PolyVertex, Prompt, Result, SelOutcome, SelectPhase, Session, Space,
|
||||
Spline, Step, Value, Vec2, bad, bool_or, curves, f64_req, json, line, number, other, point_req, set_kind, str_param, targets,
|
||||
};
|
||||
|
||||
/// The spline after a JSON SPLINEDIT option (`option` plus that option's parameters).
|
||||
pub(super) fn run_splinedit(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
|
||||
@@ -13,7 +13,8 @@ use cadcraft_geom::{TAU, Vec2, Vec3, ccw_sweep, norm_angle};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::machines::{SelOutcome, SelectPhase, number};
|
||||
use super::*;
|
||||
use super::{CommandSpec, bad, targets};
|
||||
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
|
||||
@@ -14,7 +14,8 @@ use cadcraft_doc::{DevicePaper, Orientation, PageSetup, PlotOutput, PlotterConfi
|
||||
use cadcraft_geom::{Bounds2, Vec2};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::*;
|
||||
use super::{CommandSpec, Result, always, bad, has_doc, point_req, str_param};
|
||||
|
||||
use crate::{Accept, EngineError, Input, Interactive, Prompt, Session, Step};
|
||||
|
||||
/// Where plotter configuration files live. Names are file names ending in
|
||||
|
||||
@@ -10,7 +10,8 @@ use cadcraft_doc::{EntityKind, Lineweight, Transparency};
|
||||
use cadcraft_geom::Vec3;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::*;
|
||||
use super::{CommandSpec, always, bad, bool_or, f64_req, find_command, ok, point_req, point_value, str_param, targets};
|
||||
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
@@ -1177,10 +1178,9 @@ fn run_rename(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::Session;
|
||||
|
||||
#[test]
|
||||
fn properties_set_rejects_unloaded_linetype() {
|
||||
let mut s = Session::new();
|
||||
|
||||
@@ -7,7 +7,8 @@ use serde_json::{Value, json};
|
||||
|
||||
use super::super::helpers::support_text;
|
||||
use super::super::qselect::wildcard;
|
||||
use super::*;
|
||||
use super::{Result, Session, bad, bool_or, str_param};
|
||||
|
||||
use crate::{Accept, Input, Interactive, Prompt, Step};
|
||||
|
||||
/// Names no file may define: they are not linetype definitions.
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
use cadcraft_doc::{Entity, EntityKind, Handle};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::super::*;
|
||||
use super::super::{bad, targets};
|
||||
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
/// Which properties MATCHPROP copies (its Settings).
|
||||
|
||||
@@ -5,7 +5,8 @@ use cadcraft_color::Color;
|
||||
use cadcraft_doc::{Drawing, Entity, Handle};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::*;
|
||||
use super::{CommandSpec, bad, str_param};
|
||||
|
||||
use crate::{Result, Session};
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
@@ -320,9 +321,8 @@ fn run_info(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::json;
|
||||
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
fn session() -> Session {
|
||||
let mut s = Session::new();
|
||||
|
||||
@@ -9,7 +9,8 @@ use cadcraft_doc::{Drawing, Entity, EntityKind, Handle};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::machines::{SelOutcome, SelectPhase, SelectRun};
|
||||
use super::*;
|
||||
use super::{CommandSpec, bad, targets};
|
||||
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
/// Most objects one call looks at (a hostile drawing can be huge).
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::*;
|
||||
use super::{CommandSpec, always, bad, point_param, str_param};
|
||||
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
pub(super) mod iso;
|
||||
|
||||
@@ -12,7 +12,8 @@ use cadcraft_geom::Vec2;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::helpers::v3;
|
||||
use super::*;
|
||||
use super::{CommandSpec, bad, point_req, size_param, str_param};
|
||||
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
/// Caps for hostile parameters.
|
||||
|
||||
@@ -5,7 +5,8 @@ use cadcraft_geom::{PolyVertex, Vec2};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::helpers::{line, lwpoly, v3};
|
||||
use super::*;
|
||||
use super::{CommandSpec, bad, bool_or, f64_or, point_req, str_param};
|
||||
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
|
||||
@@ -4,7 +4,8 @@ use cadcraft_doc::{EntityKind, Handle};
|
||||
use cadcraft_geom::{Segment, Vec2};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::*;
|
||||
use super::{CommandSpec, always, bad, bool_or, point_param, points_param, str_param, targets};
|
||||
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
|
||||
@@ -6,7 +6,8 @@ use cadcraft_geom::{Bounds2, Vec2};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::machines::{SelectRun, number};
|
||||
use super::*;
|
||||
use super::{CommandSpec, bad, bool_or, f64_or, f64_req, ok, point_param, point_req, str_param, targets};
|
||||
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step, View};
|
||||
|
||||
mod named_views;
|
||||
|
||||
+396
-326
@@ -249,6 +249,130 @@ fn entity(kind: &str, tags: &[Tag]) -> Option<(Common, EntityKind)> {
|
||||
}
|
||||
|
||||
/// Parse one entity while charging materialized table cells to the caller's shared import budget.
|
||||
/// An ARC, with its angles run the other way in a mirrored OCS.
|
||||
fn arc(c: &Common, t: &T) -> Arc {
|
||||
let mut a = Arc { center: ocs(c, t.p(10)), radius: t.fd(40, 1.0).abs(), start: t.fd(50, 0.0).to_radians(), end: t.fd(51, 360.0).to_radians() };
|
||||
if c.extrusion.z < 0.0 {
|
||||
// Mirrored OCS: angles run the other way.
|
||||
let (s, e) = (std::f64::consts::PI - a.end, std::f64::consts::PI - a.start);
|
||||
a.start = cadcraft_geom::norm_angle(s);
|
||||
a.end = cadcraft_geom::norm_angle(e);
|
||||
}
|
||||
a
|
||||
}
|
||||
|
||||
fn ellipse(c: &Common, t: &T) -> Ellipse {
|
||||
let mut e = Ellipse {
|
||||
center: t.p(10),
|
||||
major: t.p(11),
|
||||
ratio: t.fd(40, 1.0).clamp(1e-9, 1.0),
|
||||
start: t.fd(41, 0.0),
|
||||
end: t.fd(42, std::f64::consts::TAU),
|
||||
};
|
||||
if c.extrusion.z < 0.0 && (cadcraft_geom::ccw_sweep(e.start, e.end) - std::f64::consts::TAU).abs() > 1e-9 {
|
||||
// Centre and major axis are WCS, but the minor axis is extrusion × major: with (0,0,-1)
|
||||
// it points the other way, so parameter t lands where -t does in the XY plane. A full
|
||||
// ellipse is symmetric and stays as it is.
|
||||
let (s, e2) = (-e.end, -e.start);
|
||||
e.start = cadcraft_geom::norm_angle(s);
|
||||
e.end = cadcraft_geom::norm_angle(e2);
|
||||
}
|
||||
e
|
||||
}
|
||||
|
||||
/// A LWPOLYLINE, or the region CADCraft wrote as one.
|
||||
fn lwpolyline(tags: &[Tag], c: &Common, t: &T) -> Option<EntityKind> {
|
||||
let mut verts = Vec::new();
|
||||
let mut cur: Option<PolyVertex> = None;
|
||||
for tg in tags {
|
||||
match tg.code {
|
||||
10 => {
|
||||
if let Some(v) = cur.take() {
|
||||
verts.push(v);
|
||||
}
|
||||
cur = Some(PolyVertex { p: Vec2::new(tg.f64(), 0.0), ..Default::default() });
|
||||
}
|
||||
20 => {
|
||||
if let Some(v) = cur.as_mut() {
|
||||
v.p.y = tg.f64();
|
||||
}
|
||||
}
|
||||
40 => {
|
||||
if let Some(v) = cur.as_mut() {
|
||||
v.start_width = tg.f64();
|
||||
}
|
||||
}
|
||||
41 => {
|
||||
if let Some(v) = cur.as_mut() {
|
||||
v.end_width = tg.f64();
|
||||
}
|
||||
}
|
||||
42 => {
|
||||
if let Some(v) = cur.as_mut() {
|
||||
v.bulge = tg.f64();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
verts.extend(cur);
|
||||
let flags = t.i(70).unwrap_or(0);
|
||||
if c.extrusion.z < 0.0 {
|
||||
for v in &mut verts {
|
||||
v.p.x = -v.p.x;
|
||||
v.bulge = -v.bulge;
|
||||
}
|
||||
}
|
||||
let pl = LwPolyline { vertices: verts, closed: flags & 1 != 0, const_width: t.fd(43, 0.0), elevation: t.fd(38, 0.0), plinegen: flags & 128 != 0 };
|
||||
// A region CADCraft wrote as polylines (`dxf_region`).
|
||||
crate::dxf_region::from_polyline(tags, pl)
|
||||
}
|
||||
|
||||
fn spline(t: &T) -> Spline {
|
||||
let degree = t.i(71).unwrap_or(3).clamp(1, 10) as usize;
|
||||
let knots = t.all_f(40);
|
||||
let control = t.pts(10);
|
||||
let weights = t.all_f(41);
|
||||
let fit = t.pts(11);
|
||||
let tangent = |c: i32| t.f(c).map(|_| t.p(c).xy());
|
||||
let fit_opts =
|
||||
cadcraft_geom::FitOptions { knots: Default::default(), start_tangent: tangent(12), end_tangent: tangent(13), tolerance: t.fd(44, 0.0) }
|
||||
.sanitized();
|
||||
let closed = t.i(70).unwrap_or(0) & 1 != 0;
|
||||
let mut sp = Spline { degree, knots, control, weights, fit: fit.clone(), closed, fit_opts };
|
||||
if !sp.is_valid() && fit.len() >= 2 {
|
||||
sp = Spline::fit_with(&fit, sp.closed, fit_opts);
|
||||
} else if !sp.is_valid() && sp.control.len() >= 2 {
|
||||
sp = Spline::from_control(sp.control.clone(), degree);
|
||||
} else if fit.len() >= 3 {
|
||||
// DXF keeps the knots, not how they were spaced: recover it for later refits.
|
||||
sp.fit_opts.knots = sp.infer_knot_param();
|
||||
}
|
||||
sp
|
||||
}
|
||||
|
||||
/// An ATTDEF, whose multiline text is embedded MTEXT after its own groups.
|
||||
fn attdef(tags: &[Tag], c: &Common) -> Attrib {
|
||||
// A multiline definition has its text as embedded MTEXT after its own groups.
|
||||
let (own, embedded) = split_embedded(tags);
|
||||
let multiline = multiline_attrib(embedded, c);
|
||||
let own = T(own);
|
||||
let mut text = text_from(&own);
|
||||
if let Some(m) = &multiline {
|
||||
text.value.clone_from(&m.read_as);
|
||||
}
|
||||
Attrib {
|
||||
tag: own.s(2).unwrap_or_default(),
|
||||
text,
|
||||
invisible: own.i(70).unwrap_or(0) & 1 != 0,
|
||||
constant: own.i(70).unwrap_or(0) & 2 != 0,
|
||||
prompt: own.s(3).unwrap_or_default(),
|
||||
props: Default::default(),
|
||||
multiline,
|
||||
modes: attrib_modes(own.0),
|
||||
}
|
||||
}
|
||||
|
||||
fn entity_with_table_budget(kind: &str, tags: &[Tag], table_cells_left: &mut usize) -> Option<(Common, EntityKind)> {
|
||||
// The paper-space viewport our writer adds (dxf_write::paper_view) stays implicit.
|
||||
if kind == "VIEWPORT" && crate::dxf_ext::is_paper_view(tags) {
|
||||
@@ -260,138 +384,16 @@ fn entity_with_table_budget(kind: &str, tags: &[Tag], table_cells_left: &mut usi
|
||||
"LINE" => EntityKind::Line(Line { a: t.p(10), b: t.p(11) }),
|
||||
"POINT" => EntityKind::Point(Point { p: t.p(10), angle: t.fd(50, 0.0).to_radians() }),
|
||||
"CIRCLE" => EntityKind::Circle(Circle { center: ocs(&c, t.p(10)), radius: t.fd(40, 1.0).abs() }),
|
||||
"ARC" => {
|
||||
let mut a =
|
||||
Arc { center: ocs(&c, t.p(10)), radius: t.fd(40, 1.0).abs(), start: t.fd(50, 0.0).to_radians(), end: t.fd(51, 360.0).to_radians() };
|
||||
if c.extrusion.z < 0.0 {
|
||||
// Mirrored OCS: angles run the other way.
|
||||
let (s, e) = (std::f64::consts::PI - a.end, std::f64::consts::PI - a.start);
|
||||
a.start = cadcraft_geom::norm_angle(s);
|
||||
a.end = cadcraft_geom::norm_angle(e);
|
||||
}
|
||||
EntityKind::Arc(a)
|
||||
}
|
||||
"ELLIPSE" => {
|
||||
let mut e = Ellipse {
|
||||
center: t.p(10),
|
||||
major: t.p(11),
|
||||
ratio: t.fd(40, 1.0).clamp(1e-9, 1.0),
|
||||
start: t.fd(41, 0.0),
|
||||
end: t.fd(42, std::f64::consts::TAU),
|
||||
};
|
||||
if c.extrusion.z < 0.0 && (cadcraft_geom::ccw_sweep(e.start, e.end) - std::f64::consts::TAU).abs() > 1e-9 {
|
||||
// Centre and major axis are WCS, but the minor axis is extrusion × major: with (0,0,-1)
|
||||
// it points the other way, so parameter t lands where -t does in the XY plane. A full
|
||||
// ellipse is symmetric and stays as it is.
|
||||
let (s, e2) = (-e.end, -e.start);
|
||||
e.start = cadcraft_geom::norm_angle(s);
|
||||
e.end = cadcraft_geom::norm_angle(e2);
|
||||
}
|
||||
EntityKind::Ellipse(e)
|
||||
}
|
||||
"LWPOLYLINE" => {
|
||||
let mut verts = Vec::new();
|
||||
let mut cur: Option<PolyVertex> = None;
|
||||
for tg in tags {
|
||||
match tg.code {
|
||||
10 => {
|
||||
if let Some(v) = cur.take() {
|
||||
verts.push(v);
|
||||
}
|
||||
cur = Some(PolyVertex { p: Vec2::new(tg.f64(), 0.0), ..Default::default() });
|
||||
}
|
||||
20 => {
|
||||
if let Some(v) = cur.as_mut() {
|
||||
v.p.y = tg.f64();
|
||||
}
|
||||
}
|
||||
40 => {
|
||||
if let Some(v) = cur.as_mut() {
|
||||
v.start_width = tg.f64();
|
||||
}
|
||||
}
|
||||
41 => {
|
||||
if let Some(v) = cur.as_mut() {
|
||||
v.end_width = tg.f64();
|
||||
}
|
||||
}
|
||||
42 => {
|
||||
if let Some(v) = cur.as_mut() {
|
||||
v.bulge = tg.f64();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
verts.extend(cur);
|
||||
let flags = t.i(70).unwrap_or(0);
|
||||
if c.extrusion.z < 0.0 {
|
||||
for v in &mut verts {
|
||||
v.p.x = -v.p.x;
|
||||
v.bulge = -v.bulge;
|
||||
}
|
||||
}
|
||||
let pl = LwPolyline {
|
||||
vertices: verts,
|
||||
closed: flags & 1 != 0,
|
||||
const_width: t.fd(43, 0.0),
|
||||
elevation: t.fd(38, 0.0),
|
||||
plinegen: flags & 128 != 0,
|
||||
};
|
||||
// A region CADCraft wrote as polylines (`dxf_region`).
|
||||
crate::dxf_region::from_polyline(tags, pl)?
|
||||
}
|
||||
"SPLINE" => {
|
||||
let degree = t.i(71).unwrap_or(3).clamp(1, 10) as usize;
|
||||
let knots = t.all_f(40);
|
||||
let control = t.pts(10);
|
||||
let weights = t.all_f(41);
|
||||
let fit = t.pts(11);
|
||||
let tangent = |c: i32| t.f(c).map(|_| t.p(c).xy());
|
||||
let fit_opts = cadcraft_geom::FitOptions {
|
||||
knots: Default::default(),
|
||||
start_tangent: tangent(12),
|
||||
end_tangent: tangent(13),
|
||||
tolerance: t.fd(44, 0.0),
|
||||
}
|
||||
.sanitized();
|
||||
let closed = t.i(70).unwrap_or(0) & 1 != 0;
|
||||
let mut sp = Spline { degree, knots, control, weights, fit: fit.clone(), closed, fit_opts };
|
||||
if !sp.is_valid() && fit.len() >= 2 {
|
||||
sp = Spline::fit_with(&fit, sp.closed, fit_opts);
|
||||
} else if !sp.is_valid() && sp.control.len() >= 2 {
|
||||
sp = Spline::from_control(sp.control.clone(), degree);
|
||||
} else if fit.len() >= 3 {
|
||||
// DXF keeps the knots, not how they were spaced: recover it for later refits.
|
||||
sp.fit_opts.knots = sp.infer_knot_param();
|
||||
}
|
||||
EntityKind::Spline(sp)
|
||||
}
|
||||
"ARC" => EntityKind::Arc(arc(&c, &t)),
|
||||
"ELLIPSE" => EntityKind::Ellipse(ellipse(&c, &t)),
|
||||
"LWPOLYLINE" => lwpolyline(tags, &c, &t)?,
|
||||
"SPLINE" => EntityKind::Spline(spline(&t)),
|
||||
"HELIX" => EntityKind::Helix(helix(tags)),
|
||||
"RAY" => EntityKind::Ray(RayLine { base: t.p(10), dir: t.p(11) }),
|
||||
"XLINE" => EntityKind::XLine(RayLine { base: t.p(10), dir: t.p(11) }),
|
||||
"TEXT" => EntityKind::Text(text_from(&t)),
|
||||
"MTEXT" => EntityKind::MText(mtext_from(tags, &c)),
|
||||
"ATTDEF" => {
|
||||
// A multiline definition has its text as embedded MTEXT after its own groups.
|
||||
let (own, embedded) = split_embedded(tags);
|
||||
let multiline = multiline_attrib(embedded, &c);
|
||||
let own = T(own);
|
||||
let mut text = text_from(&own);
|
||||
if let Some(m) = &multiline {
|
||||
text.value.clone_from(&m.read_as);
|
||||
}
|
||||
EntityKind::AttDef(Attrib {
|
||||
tag: own.s(2).unwrap_or_default(),
|
||||
text,
|
||||
invisible: own.i(70).unwrap_or(0) & 1 != 0,
|
||||
constant: own.i(70).unwrap_or(0) & 2 != 0,
|
||||
prompt: own.s(3).unwrap_or_default(),
|
||||
props: Default::default(),
|
||||
multiline,
|
||||
modes: attrib_modes(own.0),
|
||||
})
|
||||
}
|
||||
"ATTDEF" => EntityKind::AttDef(attdef(tags, &c)),
|
||||
"INSERT" => {
|
||||
// A mirrored block reference comes with extrusion (0,0,-1): its insertion point, scale,
|
||||
// rotation and column spacing are in that OCS, where x runs the other way. In the WCS that
|
||||
@@ -1109,6 +1111,91 @@ fn entity_extras(kind: &str, tags: &[Tag], h: Handle, k: &mut EntityKind, rx: &m
|
||||
|
||||
/// Parse a run of entity records (ENTITIES section or a block's body) into entities. Records that
|
||||
/// can't be placed are counted in `skipped`.
|
||||
/// The entity of a POLYLINE, with its mesh faces (advancing `i` past its VERTEX records). `None`
|
||||
/// for a mesh without faces.
|
||||
fn polyline_entity(recs: &[(String, Vec<Tag>)], i: &mut usize, t: &T) -> Option<(Common, EntityKind, Vec<EntityKind>)> {
|
||||
let flags = t.i(70).unwrap_or(0);
|
||||
let mut pts: Vec<(Vec3, f64, f64, f64)> = Vec::new();
|
||||
// Polyface face records (VERTEX 70 bit 128 without 64): vertex indices 71–74.
|
||||
let mut faces: Vec<[i64; 4]> = Vec::new();
|
||||
while let Some((k2, t2)) = recs.get(*i) {
|
||||
if k2 == "VERTEX" {
|
||||
let tt = T(t2);
|
||||
let vflags = tt.i(70).unwrap_or(0);
|
||||
if vflags & 128 != 0 && vflags & 64 == 0 {
|
||||
faces.push([71, 72, 73, 74].map(|c| tt.i(c).unwrap_or(0)));
|
||||
} else if vflags & 16 == 0 {
|
||||
pts.push((tt.p(10), tt.fd(42, 0.0), tt.fd(40, 0.0), tt.fd(41, 0.0)));
|
||||
}
|
||||
*i += 1;
|
||||
} else {
|
||||
if k2 == "SEQEND" {
|
||||
*i += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
let c = common(t);
|
||||
let mut extra: Vec<EntityKind> = Vec::new();
|
||||
let k = if flags & (16 | 64) != 0 {
|
||||
// Polyface (64) and polygon (16) meshes become 3D faces; the first takes the
|
||||
// polyline's place.
|
||||
let verts: Vec<Vec3> = pts.iter().map(|p| p.0).collect();
|
||||
let mut fs = if flags & 64 != 0 { polyface_faces(&verts, &faces) } else { mesh_faces(&verts, t, flags) }.into_iter().map(EntityKind::Face3d);
|
||||
let first = fs.next()?;
|
||||
extra.extend(fs);
|
||||
first
|
||||
} else if flags & 8 != 0 {
|
||||
EntityKind::Polyline3d(Polyline3d { points: pts.iter().map(|p| p.0).collect(), closed: flags & 1 != 0 })
|
||||
} else {
|
||||
// A 2D polyline's vertices are in its OCS: mirrored like LWPOLYLINE.
|
||||
let m = if c.extrusion.z < 0.0 { -1.0 } else { 1.0 };
|
||||
EntityKind::LwPolyline(LwPolyline {
|
||||
vertices: pts.iter().map(|p| PolyVertex { p: Vec2::new(m * p.0.x, p.0.y), bulge: m * p.1, start_width: p.2, end_width: p.3 }).collect(),
|
||||
closed: flags & 1 != 0,
|
||||
const_width: t.fd(40, 0.0).min(t.fd(41, 0.0)).max(0.0),
|
||||
elevation: t.p(10).z,
|
||||
plinegen: flags & 128 != 0,
|
||||
})
|
||||
};
|
||||
Some((c, k, extra))
|
||||
}
|
||||
|
||||
/// The ATTRIB records after an INSERT, advancing `i` past them and their SEQEND.
|
||||
fn insert_attribs(recs: &[(String, Vec<Tag>)], i: &mut usize, ins_layer: &str) -> Vec<Attrib> {
|
||||
let mut attribs = Vec::new();
|
||||
while let Some((k2, t2)) = recs.get(*i) {
|
||||
if k2 == "ATTRIB" {
|
||||
let tt = T(t2);
|
||||
// A multiline attribute has its text as embedded MTEXT after its own groups.
|
||||
let (own, embedded) = split_embedded(t2);
|
||||
let multiline = multiline_attrib(embedded, &common(&tt));
|
||||
let own = T(own);
|
||||
let mut text = text_from(&own);
|
||||
if let Some(m) = &multiline {
|
||||
text.value.clone_from(&m.read_as);
|
||||
}
|
||||
attribs.push(Attrib {
|
||||
tag: own.s(2).unwrap_or_default(),
|
||||
text,
|
||||
invisible: own.i(70).unwrap_or(0) & 1 != 0,
|
||||
constant: false,
|
||||
prompt: String::new(),
|
||||
props: attrib_props(&tt, ins_layer),
|
||||
multiline,
|
||||
modes: attrib_modes(own.0),
|
||||
});
|
||||
*i += 1;
|
||||
} else {
|
||||
if k2 == "SEQEND" {
|
||||
*i += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
attribs
|
||||
}
|
||||
|
||||
fn parse_entities(
|
||||
recs: &[(String, Vec<Tag>)],
|
||||
d: &mut Drawing,
|
||||
@@ -1134,92 +1221,17 @@ fn parse_entities(
|
||||
i += 1;
|
||||
match kind.as_str() {
|
||||
"POLYLINE" => {
|
||||
let flags = t.i(70).unwrap_or(0);
|
||||
let mut pts: Vec<(Vec3, f64, f64, f64)> = Vec::new();
|
||||
// Polyface face records (VERTEX 70 bit 128 without 64): vertex indices 71–74.
|
||||
let mut faces: Vec<[i64; 4]> = Vec::new();
|
||||
while let Some((k2, t2)) = recs.get(i) {
|
||||
if k2 == "VERTEX" {
|
||||
let tt = T(t2);
|
||||
let vflags = tt.i(70).unwrap_or(0);
|
||||
if vflags & 128 != 0 && vflags & 64 == 0 {
|
||||
faces.push([71, 72, 73, 74].map(|c| tt.i(c).unwrap_or(0)));
|
||||
} else if vflags & 16 == 0 {
|
||||
pts.push((tt.p(10), tt.fd(42, 0.0), tt.fd(40, 0.0), tt.fd(41, 0.0)));
|
||||
}
|
||||
i += 1;
|
||||
} else {
|
||||
if k2 == "SEQEND" {
|
||||
i += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
let c = common(&t);
|
||||
let k = if flags & (16 | 64) != 0 {
|
||||
// Polyface (64) and polygon (16) meshes become 3D faces; the first takes the
|
||||
// polyline's place.
|
||||
let verts: Vec<Vec3> = pts.iter().map(|p| p.0).collect();
|
||||
let mut fs = if flags & 64 != 0 { polyface_faces(&verts, &faces) } else { mesh_faces(&verts, &t, flags) }
|
||||
.into_iter()
|
||||
.map(EntityKind::Face3d);
|
||||
let Some(first) = fs.next() else {
|
||||
*skipped = skipped.saturating_add(1);
|
||||
continue;
|
||||
};
|
||||
extra.extend(fs);
|
||||
first
|
||||
} else if flags & 8 != 0 {
|
||||
EntityKind::Polyline3d(Polyline3d { points: pts.iter().map(|p| p.0).collect(), closed: flags & 1 != 0 })
|
||||
} else {
|
||||
// A 2D polyline's vertices are in its OCS: mirrored like LWPOLYLINE.
|
||||
let m = if c.extrusion.z < 0.0 { -1.0 } else { 1.0 };
|
||||
EntityKind::LwPolyline(LwPolyline {
|
||||
vertices: pts
|
||||
.iter()
|
||||
.map(|p| PolyVertex { p: Vec2::new(m * p.0.x, p.0.y), bulge: m * p.1, start_width: p.2, end_width: p.3 })
|
||||
.collect(),
|
||||
closed: flags & 1 != 0,
|
||||
const_width: t.fd(40, 0.0).min(t.fd(41, 0.0)).max(0.0),
|
||||
elevation: t.p(10).z,
|
||||
plinegen: flags & 128 != 0,
|
||||
})
|
||||
let Some((c, k, more)) = polyline_entity(recs, &mut i, &t) else {
|
||||
*skipped = skipped.saturating_add(1);
|
||||
continue;
|
||||
};
|
||||
extra = more;
|
||||
kindent = Some((c, k));
|
||||
}
|
||||
"INSERT" => {
|
||||
if t.i(66) == Some(1) {
|
||||
let mut attribs = Vec::new();
|
||||
let ins_layer = kindent.as_ref().map(|(c, _)| c.layer.clone()).unwrap_or_default();
|
||||
while let Some((k2, t2)) = recs.get(i) {
|
||||
if k2 == "ATTRIB" {
|
||||
let tt = T(t2);
|
||||
// A multiline attribute has its text as embedded MTEXT after its own groups.
|
||||
let (own, embedded) = split_embedded(t2);
|
||||
let multiline = multiline_attrib(embedded, &common(&tt));
|
||||
let own = T(own);
|
||||
let mut text = text_from(&own);
|
||||
if let Some(m) = &multiline {
|
||||
text.value.clone_from(&m.read_as);
|
||||
}
|
||||
attribs.push(Attrib {
|
||||
tag: own.s(2).unwrap_or_default(),
|
||||
text,
|
||||
invisible: own.i(70).unwrap_or(0) & 1 != 0,
|
||||
constant: false,
|
||||
prompt: String::new(),
|
||||
props: attrib_props(&tt, &ins_layer),
|
||||
multiline,
|
||||
modes: attrib_modes(own.0),
|
||||
});
|
||||
i += 1;
|
||||
} else {
|
||||
if k2 == "SEQEND" {
|
||||
i += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
let attribs = insert_attribs(recs, &mut i, &ins_layer);
|
||||
if let Some((_, EntityKind::Insert(ins))) = kindent.as_mut() {
|
||||
ins.attribs = attribs;
|
||||
}
|
||||
@@ -1688,6 +1700,13 @@ pub fn read(bytes: &[u8]) -> Result<Drawing> {
|
||||
read_budgeted(bytes, 4_000_000)
|
||||
}
|
||||
|
||||
/// A block definition read from BLOCKS: name, base point, description, entities (owner, paper
|
||||
/// flag, entity) and block flags.
|
||||
type BlockDef = (String, Vec3, String, Vec<(Option<String>, bool, Entity)>, i64);
|
||||
|
||||
/// A layout read from OBJECTS: name, tab order, paper block handle and page setup.
|
||||
type LayoutDef = (String, u32, String, PageSetup);
|
||||
|
||||
/// Import DXF sections while sharing one materialized-table-cell budget across blocks and entity sections.
|
||||
fn read_budgeted(bytes: &[u8], mut table_cells_left: usize) -> Result<Drawing> {
|
||||
let tags = cadcraft_dxf::parse(bytes).map_err(|e| IoError::Format(e.to_string()))?;
|
||||
@@ -1700,8 +1719,8 @@ fn read_budgeted(bytes: &[u8], mut table_cells_left: usize) -> Result<Drawing> {
|
||||
// A file without $INSUNITS declares no units (#1209); only new drawings take the template's.
|
||||
d.header.set_i64("INSUNITS", 0);
|
||||
let mut rx = Rx { pstyles: plot_style_names(&secs), ..Rx::default() };
|
||||
let mut blocks: Vec<(String, Vec3, String, Vec<(Option<String>, bool, Entity)>, i64)> = Vec::new();
|
||||
let mut layout_objs: Vec<(String, u32, String, PageSetup)> = Vec::new();
|
||||
let mut blocks: Vec<BlockDef> = Vec::new();
|
||||
let mut layout_objs: Vec<LayoutDef> = Vec::new();
|
||||
let mut entities = Vec::new();
|
||||
let mut objs = Objects::default();
|
||||
// Every handle another program's file already uses is taken before anything gets a new one:
|
||||
@@ -1721,73 +1740,109 @@ fn read_budgeted(bytes: &[u8], mut table_cells_left: usize) -> Result<Drawing> {
|
||||
match s.name.as_str() {
|
||||
"HEADER" => header(&s.tags, &mut d),
|
||||
"TABLES" => tables(&s.tags, &mut d, &mut rx),
|
||||
"BLOCKS" => {
|
||||
let recs = records(&s.tags);
|
||||
for (k, tg) in &recs {
|
||||
let t = T(tg);
|
||||
if k == "BLOCK" && t.i(70).unwrap_or(0) & 4 != 0 {
|
||||
rx.xref_paths.insert(t.s(2).unwrap_or_default().to_ascii_uppercase(), t.s(1).unwrap_or_default());
|
||||
}
|
||||
}
|
||||
let mut n = 0;
|
||||
let mut i = 0;
|
||||
while let Some((k, tg)) = recs.get(i) {
|
||||
if k == "BLOCK" {
|
||||
let t = T(tg);
|
||||
let name = t.s(2).unwrap_or_default();
|
||||
let base = t.p(10);
|
||||
let description = t.s(4).unwrap_or_default();
|
||||
// Block flags: 4 xref, 8 overlay.
|
||||
let xref = t.i(70).unwrap_or(0) & 12;
|
||||
let mut j = i + 1;
|
||||
while recs.get(j).is_some_and(|(k, _)| k != "ENDBLK") {
|
||||
j += 1;
|
||||
}
|
||||
let body = recs.get(i + 1..j).unwrap_or(&[]);
|
||||
let ents = parse_entities(body, &mut d, &mut rx, &mut n, &mut table_cells_left);
|
||||
blocks.push((name, base, description, ents, xref));
|
||||
i = j + 1;
|
||||
} else {
|
||||
// A record outside any BLOCK ... ENDBLK pair.
|
||||
if k != "ENDBLK" {
|
||||
n = n.saturating_add(1);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
count_skipped(&mut skipped, "BLOCKS", n);
|
||||
}
|
||||
"BLOCKS" => blocks.extend(read_blocks(&s.tags, &mut d, &mut rx, &mut table_cells_left, &mut skipped)),
|
||||
// A damaged file may split its entities over several ENTITIES sections (#865).
|
||||
"ENTITIES" => {
|
||||
let mut n = 0;
|
||||
entities.extend(parse_entities(&records(&s.tags), &mut d, &mut rx, &mut n, &mut table_cells_left));
|
||||
count_skipped(&mut skipped, "ENTITIES", n);
|
||||
}
|
||||
"OBJECTS" => {
|
||||
for (k, tg) in records(&s.tags) {
|
||||
objs.add(&k, &tg);
|
||||
if k == "LAYOUT" {
|
||||
// Fields after the AcDbLayout marker (AcDbPlotSettings also has a code 1).
|
||||
let start = tg.iter().position(|x| x.code == 100 && x.str() == "AcDbLayout").unwrap_or(0);
|
||||
let tail = tg.get(start..).unwrap_or(&[]);
|
||||
let t = T(tail);
|
||||
// LAYOUT has two 330s: owner dict and the block record; take the last.
|
||||
let br = tg.iter().rfind(|x| x.code == 330).map(Tag::str).unwrap_or_default();
|
||||
let plot = T(tg.get(..start).unwrap_or(&[]));
|
||||
layout_objs.push((t.s(1).unwrap_or_default(), t.i(71).unwrap_or(0) as u32, br.to_ascii_uppercase(), page_setup(&plot)));
|
||||
}
|
||||
}
|
||||
}
|
||||
"OBJECTS" => read_objects(&s.tags, &mut objs, &mut layout_objs),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// A file CADCraft wrote names its own next handle (#495): its table records and objects
|
||||
// are numbered above it and rebuilt on save, so following $HANDSEED would drift every save.
|
||||
let own_seed = objs.xrecord_text(crate::dxf_ext::HANDSEED_KEY).and_then(|t| Handle::parse_hex(t.trim()));
|
||||
// Layouts.
|
||||
let (paper_block_for, first_layout) = setup_layouts(&mut d, &mut layout_objs, &rx);
|
||||
place_blocks(&mut d, blocks, &mut rx, &paper_block_for, &first_layout);
|
||||
place_entities(&mut d, entities, &rx, &paper_block_for, &first_layout);
|
||||
apply_sortents(&mut d, &objs.sortents, &rx.brs, &paper_block_for, &first_layout);
|
||||
objs.apply(&mut d, &rx);
|
||||
crate::dxf_image::resolve(&secs, &mut d);
|
||||
crate::dxf_mline::resolve(&secs, &mut d);
|
||||
// Keep handles unique against the header's seed.
|
||||
let seed = d.header.str("HANDSEED", "");
|
||||
if let Some(h) = own_seed.or_else(|| Handle::parse_hex(&seed)) {
|
||||
d.bump_handseed(Handle(h.0.saturating_sub(1)));
|
||||
}
|
||||
if d.layer("0").is_none() {
|
||||
d.layers.insert(0, Layer::default());
|
||||
}
|
||||
crate::dxf_raw::read(&secs, &mut d);
|
||||
d.skipped_records = skipped;
|
||||
Ok(d)
|
||||
}
|
||||
|
||||
/// Read the BLOCKS section: the xref paths of external blocks, then each block's entities.
|
||||
fn read_blocks(
|
||||
tags: &[Tag],
|
||||
d: &mut Drawing,
|
||||
rx: &mut Rx,
|
||||
table_cells_left: &mut usize,
|
||||
skipped: &mut std::collections::BTreeMap<String, usize>,
|
||||
) -> Vec<BlockDef> {
|
||||
let recs = records(tags);
|
||||
for (k, tg) in &recs {
|
||||
let t = T(tg);
|
||||
if k == "BLOCK" && t.i(70).unwrap_or(0) & 4 != 0 {
|
||||
rx.xref_paths.insert(t.s(2).unwrap_or_default().to_ascii_uppercase(), t.s(1).unwrap_or_default());
|
||||
}
|
||||
}
|
||||
let mut blocks = Vec::new();
|
||||
let mut n = 0;
|
||||
let mut i = 0;
|
||||
while let Some((k, tg)) = recs.get(i) {
|
||||
if k == "BLOCK" {
|
||||
let t = T(tg);
|
||||
let name = t.s(2).unwrap_or_default();
|
||||
let base = t.p(10);
|
||||
let description = t.s(4).unwrap_or_default();
|
||||
// Block flags: 4 xref, 8 overlay.
|
||||
let xref = t.i(70).unwrap_or(0) & 12;
|
||||
let mut j = i + 1;
|
||||
while recs.get(j).is_some_and(|(k, _)| k != "ENDBLK") {
|
||||
j += 1;
|
||||
}
|
||||
let body = recs.get(i + 1..j).unwrap_or(&[]);
|
||||
let ents = parse_entities(body, d, rx, &mut n, table_cells_left);
|
||||
blocks.push((name, base, description, ents, xref));
|
||||
i = j + 1;
|
||||
} else {
|
||||
// A record outside any BLOCK ... ENDBLK pair.
|
||||
if k != "ENDBLK" {
|
||||
n = n.saturating_add(1);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
count_skipped(skipped, "BLOCKS", n);
|
||||
blocks
|
||||
}
|
||||
|
||||
/// Read the OBJECTS section into `objs`, and each LAYOUT into `layout_objs`.
|
||||
fn read_objects(tags: &[Tag], objs: &mut Objects, layout_objs: &mut Vec<LayoutDef>) {
|
||||
for (k, tg) in records(tags) {
|
||||
objs.add(&k, &tg);
|
||||
if k == "LAYOUT" {
|
||||
// Fields after the AcDbLayout marker (AcDbPlotSettings also has a code 1).
|
||||
let start = tg.iter().position(|x| x.code == 100 && x.str() == "AcDbLayout").unwrap_or(0);
|
||||
let tail = tg.get(start..).unwrap_or(&[]);
|
||||
let t = T(tail);
|
||||
// LAYOUT has two 330s: owner dict and the block record; take the last.
|
||||
let br = tg.iter().rfind(|x| x.code == 330).map(Tag::str).unwrap_or_default();
|
||||
let plot = T(tg.get(..start).unwrap_or(&[]));
|
||||
layout_objs.push((t.s(1).unwrap_or_default(), t.i(71).unwrap_or(0) as u32, br.to_ascii_uppercase(), page_setup(&plot)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add the drawing's layouts, and return the layout each paper-space block belongs to, and the
|
||||
/// layout that takes paper-space entities without an owner.
|
||||
fn setup_layouts(d: &mut Drawing, layout_objs: &mut [LayoutDef], rx: &Rx) -> (HashMap<String, String>, String) {
|
||||
let mut paper_block_for: HashMap<String, String> = HashMap::new();
|
||||
layout_objs.sort_by_key(|l| l.1);
|
||||
for (name, order, br, page) in &layout_objs {
|
||||
for (name, order, br, page) in layout_objs.iter() {
|
||||
if name.eq_ignore_ascii_case("Model") {
|
||||
continue;
|
||||
}
|
||||
@@ -1806,7 +1861,12 @@ fn read_budgeted(bytes: &[u8], mut table_cells_left: usize) -> Result<Drawing> {
|
||||
let first_tab = d.layouts.iter().min_by_key(|l| l.tab_order).map(|l| l.name.clone());
|
||||
d.active_layout = active.clone().filter(|a| Some(a) != first_tab.as_ref());
|
||||
let first_layout = active.or_else(|| d.layouts.first().map(|l| l.name.clone())).unwrap_or_else(|| "Layout1".into());
|
||||
// Blocks: model/paper space blocks hold entities in newer files.
|
||||
(paper_block_for, first_layout)
|
||||
}
|
||||
|
||||
/// Place the BLOCKS entities: model and paper space blocks go to their space, the rest become
|
||||
/// block definitions.
|
||||
fn place_blocks(d: &mut Drawing, blocks: Vec<BlockDef>, rx: &mut Rx, paper_block_for: &HashMap<String, String>, first_layout: &str) {
|
||||
for (name, base, description, ents, xref) in blocks {
|
||||
let up = name.to_ascii_uppercase();
|
||||
if up == "*MODEL_SPACE" {
|
||||
@@ -1817,7 +1877,7 @@ fn read_budgeted(bytes: &[u8], mut table_cells_left: usize) -> Result<Drawing> {
|
||||
continue;
|
||||
}
|
||||
if up.starts_with("*PAPER_SPACE") {
|
||||
let lname = paper_block_for.get(&up).cloned().unwrap_or_else(|| first_layout.clone());
|
||||
let lname = paper_block_for.get(&up).cloned().unwrap_or_else(|| first_layout.to_string());
|
||||
if let Some(l) = d.layouts.iter_mut().find(|l| l.name == lname) {
|
||||
for (_, _, e) in ents {
|
||||
l.entities.push(e);
|
||||
@@ -1857,13 +1917,22 @@ fn read_budgeted(bytes: &[u8], mut table_cells_left: usize) -> Result<Drawing> {
|
||||
}
|
||||
d.blocks.insert(name, std::sync::Arc::new(b));
|
||||
}
|
||||
// ENTITIES section: model space, or paper space when flagged (67=1).
|
||||
}
|
||||
|
||||
/// Place the ENTITIES section: model space, or paper space when flagged (67=1).
|
||||
fn place_entities(
|
||||
d: &mut Drawing,
|
||||
entities: Vec<(Option<String>, bool, Entity)>,
|
||||
rx: &Rx,
|
||||
paper_block_for: &HashMap<String, String>,
|
||||
first_layout: &str,
|
||||
) {
|
||||
for (owner, paper, e) in entities {
|
||||
d.ensure_layer(&e.common.layer);
|
||||
let owner_name = owner.and_then(|o| rx.brs.get(&o.trim().to_ascii_uppercase()).cloned()).map(|n| n.to_ascii_uppercase());
|
||||
let to_paper = paper || owner_name.as_deref().is_some_and(|n| n.starts_with("*PAPER_SPACE"));
|
||||
if to_paper {
|
||||
let lname = owner_name.and_then(|n| paper_block_for.get(&n).cloned()).unwrap_or_else(|| first_layout.clone());
|
||||
let lname = owner_name.and_then(|n| paper_block_for.get(&n).cloned()).unwrap_or_else(|| first_layout.to_string());
|
||||
if let Some(l) = d.layouts.iter_mut().find(|l| l.name == lname) {
|
||||
l.entities.push(e);
|
||||
}
|
||||
@@ -1871,21 +1940,6 @@ fn read_budgeted(bytes: &[u8], mut table_cells_left: usize) -> Result<Drawing> {
|
||||
d.model.push(e);
|
||||
}
|
||||
}
|
||||
apply_sortents(&mut d, &objs.sortents, &rx.brs, &paper_block_for, &first_layout);
|
||||
objs.apply(&mut d, &rx);
|
||||
crate::dxf_image::resolve(&secs, &mut d);
|
||||
crate::dxf_mline::resolve(&secs, &mut d);
|
||||
// Keep handles unique against the header's seed.
|
||||
let seed = d.header.str("HANDSEED", "");
|
||||
if let Some(h) = own_seed.or_else(|| Handle::parse_hex(&seed)) {
|
||||
d.bump_handseed(Handle(h.0.saturating_sub(1)));
|
||||
}
|
||||
if d.layer("0").is_none() {
|
||||
d.layers.insert(0, Layer::default());
|
||||
}
|
||||
crate::dxf_raw::read(&secs, &mut d);
|
||||
d.skipped_records = skipped;
|
||||
Ok(d)
|
||||
}
|
||||
|
||||
fn count_skipped(skipped: &mut std::collections::BTreeMap<String, usize>, section: &str, n: usize) {
|
||||
@@ -2054,13 +2108,7 @@ impl Objects {
|
||||
Some(text)
|
||||
}
|
||||
|
||||
fn apply(self, d: &mut Drawing, rx: &Rx) {
|
||||
if let Some(v) = self.wipeout_frame {
|
||||
d.header.set_i64("WIPEOUTFRAME", v);
|
||||
}
|
||||
if let Some(style) = self.xrecord_text(crate::dxf_ext::MLEADER_STYLE_KEY).filter(|s| !s.trim().is_empty()) {
|
||||
d.header.set_str("CMLEADERSTYLE", &style);
|
||||
}
|
||||
fn apply_table_styles(&self, d: &mut Drawing) {
|
||||
// Table styles (named by their ACAD_TABLESTYLE dictionary entries).
|
||||
for (h, tags) in &self.table_styles {
|
||||
let Some(name) = self.names.get(h).filter(|n| !n.is_empty()) else { continue };
|
||||
@@ -2070,6 +2118,9 @@ impl Objects {
|
||||
None => d.table_styles.push(st),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_table_style_fixes(&self, d: &mut Drawing, rx: &Rx) {
|
||||
for (h, sh) in &rx.table_style_fix {
|
||||
let name = self.names.get(sh).cloned();
|
||||
// A table from another writer has no scale of its own: its text height is its
|
||||
@@ -2090,6 +2141,61 @@ impl Objects {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_mleader_styles(&self, d: &mut Drawing, rx: &Rx) {
|
||||
// Multileader styles (named by their ACAD_MLEADERSTYLE dictionary entries).
|
||||
for (h, tags) in &self.mleader_styles {
|
||||
let Some(name) = self.names.get(h).filter(|n| !n.is_empty()) else { continue };
|
||||
let t = T(tags);
|
||||
let def = MLeaderStyle::default();
|
||||
let num = |code: i32, d: f64, min: f64| t.f(code).filter(|v| v.is_finite() && *v >= min).unwrap_or(d);
|
||||
let text_style = t.s(342).and_then(|sh| rx.styles.get(&sh.trim().to_ascii_uppercase()).cloned()).filter(|n| !n.is_empty());
|
||||
let st = MLeaderStyle {
|
||||
name: name.clone(),
|
||||
arrow_size: num(44, def.arrow_size, 0.0),
|
||||
text_height: t.f(45).filter(|v| v.is_finite() && *v > 0.0).unwrap_or(def.text_height),
|
||||
landing_gap: num(42, def.landing_gap, 0.0),
|
||||
dogleg: num(43, def.dogleg, 0.0),
|
||||
text_style: text_style.unwrap_or(def.text_style),
|
||||
annotative: t.i(296).unwrap_or(0) != 0,
|
||||
};
|
||||
match d.mleader_styles.iter_mut().find(|s| s.name.eq_ignore_ascii_case(name)) {
|
||||
Some(x) => *x = st,
|
||||
None => d.mleader_styles.push(st),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_groups(&self, d: &mut Drawing) {
|
||||
// Groups (named by their ACAD_GROUP dictionary entries); members that exist.
|
||||
for (h, tags) in &self.groups {
|
||||
let Some(name) = self.names.get(h).filter(|n| !n.is_empty()) else { continue };
|
||||
let t = T(tags);
|
||||
let members: Vec<Handle> = tags
|
||||
.iter()
|
||||
.filter(|x| x.code == 340)
|
||||
.filter_map(|x| Handle::parse_hex(&x.str()))
|
||||
.filter(|m| d.entity(*m).is_some())
|
||||
.take(MAX_OBJECTS)
|
||||
.collect();
|
||||
let g = Group { name: name.clone(), description: t.s(300).unwrap_or_default(), selectable: t.i(71).unwrap_or(1) != 0, members };
|
||||
match d.groups.iter_mut().find(|x| x.name.eq_ignore_ascii_case(name)) {
|
||||
Some(x) => *x = g,
|
||||
None => d.groups.push(g),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply(self, d: &mut Drawing, rx: &Rx) {
|
||||
if let Some(v) = self.wipeout_frame {
|
||||
d.header.set_i64("WIPEOUTFRAME", v);
|
||||
}
|
||||
if let Some(style) = self.xrecord_text(crate::dxf_ext::MLEADER_STYLE_KEY).filter(|s| !s.trim().is_empty()) {
|
||||
d.header.set_str("CMLEADERSTYLE", &style);
|
||||
}
|
||||
self.apply_table_styles(d);
|
||||
self.apply_table_style_fixes(d, rx);
|
||||
// Parametric constraints and parameters.
|
||||
if let Some((c, p)) = self.xrecord_text(crate::dxf_ext::CONSTRAINTS_KEY).and_then(|t| crate::dxf_ext::parse_constraints(&t)) {
|
||||
d.constraints = c;
|
||||
@@ -2113,50 +2219,14 @@ impl Objects {
|
||||
if let Some(tables) = self.xrecord_text(crate::dxf_ext::PLOT_STYLES_KEY).and_then(|t| crate::dxf_ext::parse_plot_styles(&t)) {
|
||||
d.plot_style_tables = tables;
|
||||
}
|
||||
// Multileader styles (named by their ACAD_MLEADERSTYLE dictionary entries).
|
||||
for (h, tags) in &self.mleader_styles {
|
||||
let Some(name) = self.names.get(h).filter(|n| !n.is_empty()) else { continue };
|
||||
let t = T(tags);
|
||||
let def = MLeaderStyle::default();
|
||||
let num = |code: i32, d: f64, min: f64| t.f(code).filter(|v| v.is_finite() && *v >= min).unwrap_or(d);
|
||||
let text_style = t.s(342).and_then(|sh| rx.styles.get(&sh.trim().to_ascii_uppercase()).cloned()).filter(|n| !n.is_empty());
|
||||
let st = MLeaderStyle {
|
||||
name: name.clone(),
|
||||
arrow_size: num(44, def.arrow_size, 0.0),
|
||||
text_height: t.f(45).filter(|v| v.is_finite() && *v > 0.0).unwrap_or(def.text_height),
|
||||
landing_gap: num(42, def.landing_gap, 0.0),
|
||||
dogleg: num(43, def.dogleg, 0.0),
|
||||
text_style: text_style.unwrap_or(def.text_style),
|
||||
annotative: t.i(296).unwrap_or(0) != 0,
|
||||
};
|
||||
match d.mleader_styles.iter_mut().find(|s| s.name.eq_ignore_ascii_case(name)) {
|
||||
Some(x) => *x = st,
|
||||
None => d.mleader_styles.push(st),
|
||||
}
|
||||
}
|
||||
self.apply_mleader_styles(d, rx);
|
||||
crate::dxf_mleader::apply(d, &rx.mleader_fix, &self.names);
|
||||
for (name, view) in &self.layout_views {
|
||||
if let Some(l) = d.layouts.iter_mut().find(|l| l.name == *name) {
|
||||
l.view = Some(*view);
|
||||
}
|
||||
}
|
||||
// Groups (named by their ACAD_GROUP dictionary entries); members that exist.
|
||||
for (h, tags) in &self.groups {
|
||||
let Some(name) = self.names.get(h).filter(|n| !n.is_empty()) else { continue };
|
||||
let t = T(tags);
|
||||
let members: Vec<Handle> = tags
|
||||
.iter()
|
||||
.filter(|x| x.code == 340)
|
||||
.filter_map(|x| Handle::parse_hex(&x.str()))
|
||||
.filter(|m| d.entity(*m).is_some())
|
||||
.take(MAX_OBJECTS)
|
||||
.collect();
|
||||
let g = Group { name: name.clone(), description: t.s(300).unwrap_or_default(), selectable: t.i(71).unwrap_or(1) != 0, members };
|
||||
match d.groups.iter_mut().find(|x| x.name.eq_ignore_ascii_case(name)) {
|
||||
Some(x) => *x = g,
|
||||
None => d.groups.push(g),
|
||||
}
|
||||
}
|
||||
self.apply_groups(d);
|
||||
// Standard associativity, for dimensions without CADCraft's exact links.
|
||||
for tags in &self.dimassocs {
|
||||
dimassoc(d, tags);
|
||||
|
||||
+11
-11
@@ -2373,7 +2373,7 @@ fn tags_for(d: &Drawing, version: Version, target: Version) -> Vec<Tag> {
|
||||
// ---------------- BLOCKS ----------------
|
||||
w.s(0, "SECTION");
|
||||
w.s(2, "BLOCKS");
|
||||
let block = |w: &mut W, name: &str, desc: &str, brh: &str, base: Vec3, flags: i64, ents: &mut dyn Iterator<Item = &Entity>, paper: bool| {
|
||||
let block = |w: &mut W, (name, desc): (&str, &str), brh: &str, base: Vec3, flags: i64, ents: &mut dyn Iterator<Item = &Entity>, paper: bool| {
|
||||
let bh = w.h();
|
||||
w.s(0, "BLOCK");
|
||||
w.s(5, bh);
|
||||
@@ -2406,16 +2406,16 @@ fn tags_for(d: &Drawing, version: Version, target: Version) -> Vec<Tag> {
|
||||
w.s(8, "0");
|
||||
w.s(100, "AcDbBlockEnd");
|
||||
};
|
||||
block(&mut w, "*Model_Space", "", &ms_br, Vec3::ZERO, 0, &mut std::iter::empty(), false);
|
||||
block(&mut w, ("*Model_Space", ""), &ms_br, Vec3::ZERO, 0, &mut std::iter::empty(), false);
|
||||
for (i, (n, h, l)) in ps_brs.iter().enumerate() {
|
||||
if i == 0 {
|
||||
block(&mut w, n, "", h, Vec3::ZERO, 0, &mut std::iter::empty(), true);
|
||||
block(&mut w, (n, ""), h, Vec3::ZERO, 0, &mut std::iter::empty(), true);
|
||||
} else {
|
||||
block(&mut w, n, "", h, Vec3::ZERO, 0, &mut sheet_first(l).into_iter(), true);
|
||||
block(&mut w, (n, ""), h, Vec3::ZERO, 0, &mut sheet_first(l).into_iter(), true);
|
||||
}
|
||||
}
|
||||
for (n, h, b) in &user_blocks {
|
||||
block(&mut w, n, &b.description, h, b.base, if b.anonymous { 1 } else { 0 }, &mut b.entities.iter().map(|e| e.as_ref()), false);
|
||||
block(&mut w, (n, &b.description), h, b.base, if b.anonymous { 1 } else { 0 }, &mut b.entities.iter().map(|e| e.as_ref()), false);
|
||||
}
|
||||
// Generated geometry entities (dimension, arrowhead and table blocks) need handles.
|
||||
for (_, _, ents) in dim_defs.iter_mut().chain(arrow_defs.iter_mut()).chain(table_defs.iter_mut()) {
|
||||
@@ -2425,13 +2425,13 @@ fn tags_for(d: &Drawing, version: Version, target: Version) -> Vec<Tag> {
|
||||
}
|
||||
}
|
||||
for (n, h, ents) in &dim_defs {
|
||||
block(&mut w, n, "", h, Vec3::ZERO, 1, &mut ents.iter(), false);
|
||||
block(&mut w, (n, ""), h, Vec3::ZERO, 1, &mut ents.iter(), false);
|
||||
}
|
||||
for (n, h, ents) in &arrow_defs {
|
||||
block(&mut w, n, "", h, Vec3::ZERO, 0, &mut ents.iter(), false);
|
||||
block(&mut w, (n, ""), h, Vec3::ZERO, 0, &mut ents.iter(), false);
|
||||
}
|
||||
for (n, h, ents) in &table_defs {
|
||||
block(&mut w, n, "", h, Vec3::ZERO, 1, &mut ents.iter(), false);
|
||||
block(&mut w, (n, ""), h, Vec3::ZERO, 1, &mut ents.iter(), false);
|
||||
}
|
||||
w.s(0, "ENDSEC");
|
||||
|
||||
@@ -2612,7 +2612,7 @@ fn tags_for(d: &Drawing, version: Version, target: Version) -> Vec<Tag> {
|
||||
w.s(3, &l.name);
|
||||
w.s(350, layout_handles.get(i).cloned().unwrap_or_default());
|
||||
}
|
||||
let layout_obj = |w: &mut W, h: &str, name: &str, tab: i64, brh: &str, flags: i64, page: &PageSetup, view: Option<(Vec2, f64)>| {
|
||||
let layout_obj = |w: &mut W, h: &str, (name, tab): (&str, i64), brh: &str, flags: i64, page: &PageSetup, view: Option<(Vec2, f64)>| {
|
||||
// Plot scale: a standard scale when fitting or 1:1, otherwise custom (142 paper units
|
||||
// per 143 drawing units).
|
||||
let scale = if page.scale.is_finite() && page.scale > 0.0 { page.scale } else { 1.0 };
|
||||
@@ -2697,10 +2697,10 @@ fn tags_for(d: &Drawing, version: Version, target: Version) -> Vec<Tag> {
|
||||
// plot type 5, "Layout information" (DXF Reference, PLOTSETTINGS group 74), which means
|
||||
// nothing for model space: another program's Plot dialog then shows no plot area or scale.
|
||||
let model_page = PageSetup { plot_area: "extents".into(), scale_to_fit: true, ..PageSetup::default() };
|
||||
layout_obj(&mut w, &model_layout, "Model", 0, &ms_br, 1, &model_page, None);
|
||||
layout_obj(&mut w, &model_layout, ("Model", 0), &ms_br, 1, &model_page, None);
|
||||
for (i, (_, brh, l)) in ps_brs.iter().enumerate() {
|
||||
let h = layout_handles.get(i).cloned().unwrap_or_default();
|
||||
layout_obj(&mut w, &h, &l.name, i64::from(l.tab_order.max(1)), brh, 1, &l.page, l.view);
|
||||
layout_obj(&mut w, &h, (&l.name, i64::from(l.tab_order.max(1))), brh, 1, &l.page, l.view);
|
||||
}
|
||||
// Raster image definitions.
|
||||
w.t.extend(crate::dxf_image::objects(&cx.images, &root_dict));
|
||||
|
||||
+110
-84
@@ -17,7 +17,7 @@ use std::fmt::Write as _;
|
||||
use cadcraft_color::Rgb;
|
||||
use cadcraft_doc::{Drawing, Handle, PageSetup, Space};
|
||||
use cadcraft_geom::{Bounds2, Vec2};
|
||||
use cadcraft_render::{Kind, Sheet, clip, paper};
|
||||
use cadcraft_render::{DPrim, Kind, Sheet, clip, paper};
|
||||
use serde_json::Value;
|
||||
|
||||
use craft_pdf::write::backend as kr;
|
||||
@@ -808,7 +808,7 @@ fn krilla_failure(e: KrillaError) -> Failure {
|
||||
/// as text instead: the glyph at font size 1 under its glyph matrix (the matrix axes are one
|
||||
/// em).
|
||||
fn marks(s: &mut Surface<'_>, p: &SheetPage, groups: &Groups, fonts: &mut fonts::Fonts) -> std::result::Result<(), Failure> {
|
||||
let (list, map) = (&p.list, &p.map);
|
||||
let list = &p.list;
|
||||
let texts = fonts::texts(list);
|
||||
let white = Rgb(255, 255, 255);
|
||||
let mut batch: Option<(StrokeStyle, PathBuilder)> = None;
|
||||
@@ -829,100 +829,126 @@ fn marks(s: &mut Surface<'_>, p: &SheetPage, groups: &Groups, fonts: &mut fonts:
|
||||
let t = if prim.kind == Kind::Mask { 0 } else { prim.transparency.min(cadcraft_render::MAX_TRANSPARENCY) };
|
||||
let w = f64::from(prim.lw) * PT_PER_MM;
|
||||
let w = if w.is_finite() && w > 0.0 { w.min(100.0) } else { 0.0 };
|
||||
let m = Mark { rgb, t, w };
|
||||
match prim.kind {
|
||||
Kind::Polyline | Kind::Infinite { .. } => {
|
||||
let pts: Vec<Vec2> = match prim.kind {
|
||||
Kind::Infinite { ray } => match (raw.first(), raw.get(1)) {
|
||||
(Some(base), Some(dir)) => {
|
||||
let a = map.pt(*base);
|
||||
let dir_pt = map.pt(*base + *dir) - a;
|
||||
match clip::clip_infinite(a, dir_pt, ray, &p.clip_pt) {
|
||||
Some((x, y)) => vec![x, y],
|
||||
None => continue,
|
||||
}
|
||||
}
|
||||
_ => continue,
|
||||
},
|
||||
_ => raw.iter().map(|q| map.pt(*q)).collect(),
|
||||
};
|
||||
if pts.len() < 2 {
|
||||
continue;
|
||||
}
|
||||
// With a vertex out of range, keep the pieces inside the plot area, widened so
|
||||
// a wide stroke just outside it still shows its edge.
|
||||
let runs = if pts.iter().all(|q| coord(*q).is_some()) { vec![pts] } else { clip::clip_polyline(&pts, &p.clip_pt.expand(w + 1.0)) };
|
||||
let style = StrokeStyle { rgb, width: w as f32, transparency: t };
|
||||
if t > 0 || batch.as_ref().is_some_and(|(b, _)| *b != style) {
|
||||
flush(s, &mut batch);
|
||||
}
|
||||
for run in runs {
|
||||
let Some(pts) = run.iter().map(|q| coord(*q)).collect::<Option<Vec<_>>>() else { continue };
|
||||
// The writer leaves out a path without extent, so a zero-length line is
|
||||
// painted as the round-cap dot its stroke would make.
|
||||
if let Some(&(x, y)) = pts.first()
|
||||
&& pts.iter().all(|q| *q == (x, y))
|
||||
{
|
||||
flush(s, &mut batch);
|
||||
let d = if w > 0.0 { w as f32 } else { HAIRLINE_DOT_PT };
|
||||
if let Some(path) = circle_path(x, y, d / 2.0) {
|
||||
fill_path(s, &path, rgb, t);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let (_, pb) = batch.get_or_insert_with(|| (style, PathBuilder::new()));
|
||||
for (j, (x, y)) in pts.iter().enumerate() {
|
||||
if j == 0 {
|
||||
pb.move_to(*x, *y);
|
||||
} else {
|
||||
pb.line_to(*x, *y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Kind::Polyline | Kind::Infinite { .. } => polyline(s, &mut batch, p, prim, raw, m),
|
||||
Kind::Tris | Kind::Mask => {
|
||||
flush(s, &mut batch);
|
||||
// A wipeout's mask is paper white.
|
||||
let rgb = if prim.kind == Kind::Mask { white } else { rgb };
|
||||
if let Some(text) = u32::try_from(i).ok().and_then(|i| texts.get(&i))
|
||||
&& let Some(font) = fonts.font(&text.glyph.face)
|
||||
{
|
||||
glyph(s, font, text, map, rgb, t);
|
||||
continue;
|
||||
}
|
||||
let mapped: Vec<Vec2> = raw.iter().map(|q| map.pt(*q)).collect();
|
||||
// With a vertex out of range, keep the parts inside the plot area.
|
||||
let tris = if mapped.iter().all(|q| coord(*q).is_some()) { mapped } else { clip::clip_triangles(&mapped, &p.clip_pt) };
|
||||
let mut pb = PathBuilder::new();
|
||||
for tri in tris.as_chunks::<3>().0 {
|
||||
let Some(v) = tri.iter().map(|q| coord(*q)).collect::<Option<Vec<_>>>() else { continue };
|
||||
for (j, (x, y)) in v.iter().enumerate() {
|
||||
if j == 0 {
|
||||
pb.move_to(*x, *y);
|
||||
} else {
|
||||
pb.line_to(*x, *y);
|
||||
}
|
||||
}
|
||||
pb.close();
|
||||
}
|
||||
if let Some(path) = pb.finish() {
|
||||
fill_path(s, &path, rgb, t);
|
||||
}
|
||||
}
|
||||
Kind::Point => {
|
||||
let Some(q) = raw.first() else { continue };
|
||||
flush(s, &mut batch);
|
||||
let half = (w.max(0.5)) / 2.0;
|
||||
let c = map.pt(*q);
|
||||
if let Some(path) = rect_path(c - Vec2::new(half, half), c + Vec2::new(half, half)) {
|
||||
fill_path(s, &path, rgb, t);
|
||||
let m = if prim.kind == Kind::Mask { Mark { rgb: white, ..m } } else { m };
|
||||
let text_font =
|
||||
u32::try_from(i).ok().and_then(|i| texts.get(&i).copied()).and_then(|text| fonts.font(&text.glyph.face).map(|font| (text, font)));
|
||||
match text_font {
|
||||
Some((text, font)) => glyph(s, font, text, &p.map, m.rgb, m.t),
|
||||
None => fill_tris(s, p, raw, m.rgb, m.t),
|
||||
}
|
||||
}
|
||||
Kind::Point => point(s, &mut batch, &p.map, raw, m),
|
||||
}
|
||||
}
|
||||
flush(s, &mut batch);
|
||||
layer_changed(s, layer, None)
|
||||
}
|
||||
|
||||
/// How a primitive is painted: colour, transparency (percent) and lineweight (points).
|
||||
#[derive(Clone, Copy)]
|
||||
struct Mark {
|
||||
rgb: Rgb,
|
||||
t: u8,
|
||||
w: f64,
|
||||
}
|
||||
|
||||
/// Stroke a polyline or infinite line, clipped to the plot area, into the open batch.
|
||||
fn polyline(s: &mut Surface<'_>, batch: &mut Option<(StrokeStyle, PathBuilder)>, p: &SheetPage, prim: &DPrim, raw: &[Vec2], m: Mark) {
|
||||
let Some(pts) = polyline_points(prim, raw, &p.map, &p.clip_pt) else { return };
|
||||
if pts.len() < 2 {
|
||||
return;
|
||||
}
|
||||
// With a vertex out of range, keep the pieces inside the plot area, widened so
|
||||
// a wide stroke just outside it still shows its edge.
|
||||
let runs = if pts.iter().all(|q| coord(*q).is_some()) { vec![pts] } else { clip::clip_polyline(&pts, &p.clip_pt.expand(m.w + 1.0)) };
|
||||
let style = StrokeStyle { rgb: m.rgb, width: m.w as f32, transparency: m.t };
|
||||
if m.t > 0 || batch.as_ref().is_some_and(|(b, _)| *b != style) {
|
||||
flush(s, batch);
|
||||
}
|
||||
for run in runs {
|
||||
stroke_run(s, batch, &run, style, m);
|
||||
}
|
||||
}
|
||||
|
||||
/// The page points of a polyline, or of an infinite line clipped to the plot area; `None` when
|
||||
/// there is nothing to draw.
|
||||
fn polyline_points(prim: &DPrim, raw: &[Vec2], map: &Map, clip_pt: &Bounds2) -> Option<Vec<Vec2>> {
|
||||
match prim.kind {
|
||||
Kind::Infinite { ray } => {
|
||||
let (Some(base), Some(dir)) = (raw.first(), raw.get(1)) else { return None };
|
||||
let a = map.pt(*base);
|
||||
let dir_pt = map.pt(*base + *dir) - a;
|
||||
let (x, y) = clip::clip_infinite(a, dir_pt, ray, clip_pt)?;
|
||||
Some(vec![x, y])
|
||||
}
|
||||
_ => Some(raw.iter().map(|q| map.pt(*q)).collect()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add one run of a polyline to the batch, or paint it as a dot when it has no extent.
|
||||
fn stroke_run(s: &mut Surface<'_>, batch: &mut Option<(StrokeStyle, PathBuilder)>, run: &[Vec2], style: StrokeStyle, m: Mark) {
|
||||
let Some(pts) = run.iter().map(|q| coord(*q)).collect::<Option<Vec<_>>>() else { return };
|
||||
// The writer leaves out a path without extent, so a zero-length line is
|
||||
// painted as the round-cap dot its stroke would make.
|
||||
if let Some(&(x, y)) = pts.first()
|
||||
&& pts.iter().all(|q| *q == (x, y))
|
||||
{
|
||||
flush(s, batch);
|
||||
let d = if m.w > 0.0 { m.w as f32 } else { HAIRLINE_DOT_PT };
|
||||
if let Some(path) = circle_path(x, y, d / 2.0) {
|
||||
fill_path(s, &path, m.rgb, m.t);
|
||||
}
|
||||
return;
|
||||
}
|
||||
let (_, pb) = batch.get_or_insert_with(|| (style, PathBuilder::new()));
|
||||
for (j, (x, y)) in pts.iter().enumerate() {
|
||||
if j == 0 {
|
||||
pb.move_to(*x, *y);
|
||||
} else {
|
||||
pb.line_to(*x, *y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill a set of triangles, clipped to the plot area.
|
||||
fn fill_tris(s: &mut Surface<'_>, p: &SheetPage, raw: &[Vec2], rgb: Rgb, t: u8) {
|
||||
let mapped: Vec<Vec2> = raw.iter().map(|q| p.map.pt(*q)).collect();
|
||||
// With a vertex out of range, keep the parts inside the plot area.
|
||||
let tris = if mapped.iter().all(|q| coord(*q).is_some()) { mapped } else { clip::clip_triangles(&mapped, &p.clip_pt) };
|
||||
let mut pb = PathBuilder::new();
|
||||
for tri in tris.as_chunks::<3>().0 {
|
||||
let Some(v) = tri.iter().map(|q| coord(*q)).collect::<Option<Vec<_>>>() else { continue };
|
||||
for (j, (x, y)) in v.iter().enumerate() {
|
||||
if j == 0 {
|
||||
pb.move_to(*x, *y);
|
||||
} else {
|
||||
pb.line_to(*x, *y);
|
||||
}
|
||||
}
|
||||
pb.close();
|
||||
}
|
||||
if let Some(path) = pb.finish() {
|
||||
fill_path(s, &path, rgb, t);
|
||||
}
|
||||
}
|
||||
|
||||
/// Paint a point marker: a small square at the point.
|
||||
fn point(s: &mut Surface<'_>, batch: &mut Option<(StrokeStyle, PathBuilder)>, map: &Map, raw: &[Vec2], m: Mark) {
|
||||
let Some(q) = raw.first() else { return };
|
||||
flush(s, batch);
|
||||
let half = (m.w.max(0.5)) / 2.0;
|
||||
let c = map.pt(*q);
|
||||
if let Some(path) = rect_path(c - Vec2::new(half, half), c + Vec2::new(half, half)) {
|
||||
fill_path(s, &path, m.rgb, m.t);
|
||||
}
|
||||
}
|
||||
|
||||
/// Write one glyph as text in its embedded font: font size 1, placed by the glyph's matrix (one
|
||||
/// em along each axis). A glyph whose matrix is out of range or collapses is left out.
|
||||
fn glyph(s: &mut Surface<'_>, font: kr::text::Font, t: &cadcraft_render::PlotText, map: &Map, rgb: Rgb, transparency: u8) {
|
||||
|
||||
+14
-14
@@ -165,7 +165,7 @@ fn style(d: &mut Drawing, name: &str, font: &str) {
|
||||
d.text_styles.push(TextStyle { name: name.into(), font: font.into(), ..TextStyle::default() });
|
||||
}
|
||||
|
||||
fn text(d: &mut Drawing, space: &Space, style: &str, at: (f64, f64), value: &str, rotation: f64, width_factor: f64, oblique: f64) {
|
||||
fn text(d: &mut Drawing, space: &Space, style: &str, at: (f64, f64), value: &str, (rotation, width_factor, oblique): (f64, f64, f64)) {
|
||||
let t = Text {
|
||||
insert: Vec3::new(at.0, at.1, 0.0),
|
||||
align_pt: None,
|
||||
@@ -225,7 +225,7 @@ fn font_files(doc: &Doc) -> usize {
|
||||
#[test]
|
||||
fn truetype_text_is_embedded_as_text() {
|
||||
let mut d = drawing();
|
||||
text(&mut d, &Space::Model, "Real", (0.0, 0.0), "ABBA", 0.0, 1.0, 0.0);
|
||||
text(&mut d, &Space::Model, "Real", (0.0, 0.0), "ABBA", (0.0, 1.0, 0.0));
|
||||
let doc = pdf(&d, &Space::Model, json!({}));
|
||||
let page = &doc.pages[0];
|
||||
// A Type0 font over a CIDFontType2 subset with a ToUnicode map.
|
||||
@@ -260,7 +260,7 @@ fn truetype_text_is_embedded_as_text() {
|
||||
#[test]
|
||||
fn the_subset_is_a_valid_truetype_font() {
|
||||
let mut d = drawing();
|
||||
text(&mut d, &Space::Model, "Real", (0.0, 0.0), "A", 0.0, 1.0, 0.0);
|
||||
text(&mut d, &Space::Model, "Real", (0.0, 0.0), "A", (0.0, 1.0, 0.0));
|
||||
let doc = pdf(&d, &Space::Model, json!({}));
|
||||
let file = doc.pages[0].fonts[0].font_file.clone().expect("an embedded font file");
|
||||
let face = cadcraft_fonts::ttf::Face::new(std::sync::Arc::new(file.clone()), 0).expect("the subset parses");
|
||||
@@ -273,9 +273,9 @@ fn the_subset_is_a_valid_truetype_font() {
|
||||
fn rotation_width_factor_and_obliquing_go_into_the_text_matrix() {
|
||||
let mut d = drawing();
|
||||
let quarter = std::f64::consts::FRAC_PI_2;
|
||||
text(&mut d, &Space::Model, "Real", (0.0, 0.0), "A", quarter, 1.0, 0.0);
|
||||
text(&mut d, &Space::Model, "Real", (0.0, 50.0), "A", 0.0, 2.0, 0.0);
|
||||
text(&mut d, &Space::Model, "Real", (0.0, 100.0), "A", 0.0, 1.0, 15f64.to_radians());
|
||||
text(&mut d, &Space::Model, "Real", (0.0, 0.0), "A", (quarter, 1.0, 0.0));
|
||||
text(&mut d, &Space::Model, "Real", (0.0, 50.0), "A", (0.0, 2.0, 0.0));
|
||||
text(&mut d, &Space::Model, "Real", (0.0, 100.0), "A", (0.0, 1.0, 15f64.to_radians()));
|
||||
let m = matrices(&pdf(&d, &Space::Model, json!({})));
|
||||
assert_eq!(m.len(), 3, "{m:?}");
|
||||
// Rotated a quarter turn: the baseline points up, "up" points left.
|
||||
@@ -294,7 +294,7 @@ fn rotation_width_factor_and_obliquing_go_into_the_text_matrix() {
|
||||
#[test]
|
||||
fn a_font_the_writer_cannot_subset_plots_as_outlines() {
|
||||
let mut d = drawing();
|
||||
text(&mut d, &Space::Model, "Short", (0.0, 0.0), "AB", 0.0, 1.0, 0.0);
|
||||
text(&mut d, &Space::Model, "Short", (0.0, 0.0), "AB", (0.0, 1.0, 0.0));
|
||||
let p = pdf(&d, &Space::Model, json!({}));
|
||||
assert!(font_files(&p) == 0 && pdf_read::count(p.content(), "BT") == 0 && fills(&p) > 0, "outlines: {}", p.content());
|
||||
}
|
||||
@@ -302,16 +302,16 @@ fn a_font_the_writer_cannot_subset_plots_as_outlines() {
|
||||
#[test]
|
||||
fn fonts_that_forbid_embedding_and_the_geometry_option_draw_outlines() {
|
||||
let mut d = drawing();
|
||||
text(&mut d, &Space::Model, "Locked", (0.0, 0.0), "AB", 0.0, 1.0, 0.0);
|
||||
text(&mut d, &Space::Model, "Locked", (0.0, 0.0), "AB", (0.0, 1.0, 0.0));
|
||||
let p = pdf(&d, &Space::Model, json!({}));
|
||||
assert!(font_files(&p) == 0 && pdf_read::count(p.content(), "BT") == 0 && fills(&p) > 0, "restricted licence: outlines");
|
||||
let mut d = drawing();
|
||||
text(&mut d, &Space::Model, "Real", (0.0, 0.0), "AB", 0.0, 1.0, 0.0);
|
||||
text(&mut d, &Space::Model, "Real", (0.0, 0.0), "AB", (0.0, 1.0, 0.0));
|
||||
let p = pdf(&d, &Space::Model, json!({"text": "geometry"}));
|
||||
assert!(font_files(&p) == 0 && pdf_read::count(p.content(), "BT") == 0 && fills(&p) > 0, "text as geometry");
|
||||
// The stroke font is always geometry.
|
||||
let mut d = drawing();
|
||||
text(&mut d, &Space::Model, "Standard", (0.0, 0.0), "AB", 0.0, 1.0, 0.0);
|
||||
text(&mut d, &Space::Model, "Standard", (0.0, 0.0), "AB", (0.0, 1.0, 0.0));
|
||||
let p = pdf(&d, &Space::Model, json!({}));
|
||||
assert!(pdf_read::count(p.content(), "BT") == 0 && pdf_read::count(p.content(), "S") > 0);
|
||||
}
|
||||
@@ -321,8 +321,8 @@ fn text_seen_through_a_viewport_and_on_layers() {
|
||||
let mut d = drawing();
|
||||
let paper = Space::Paper("Layout1".into());
|
||||
// Model: one word well inside the viewport, one cut by its right edge.
|
||||
text(&mut d, &Space::Model, "Real", (0.0, 0.0), "AB", 0.0, 1.0, 0.0);
|
||||
text(&mut d, &Space::Model, "Real", (88.0, 0.0), "BA", 0.0, 1.0, 0.0);
|
||||
text(&mut d, &Space::Model, "Real", (0.0, 0.0), "AB", (0.0, 1.0, 0.0));
|
||||
text(&mut d, &Space::Model, "Real", (88.0, 0.0), "BA", (0.0, 1.0, 0.0));
|
||||
let vp = Viewport {
|
||||
center: Vec3::new(150.0, 100.0, 0.0),
|
||||
width: 100.0,
|
||||
@@ -337,7 +337,7 @@ fn text_seen_through_a_viewport_and_on_layers() {
|
||||
on: true,
|
||||
};
|
||||
d.add(&paper, Common::default(), EntityKind::Viewport(vp)).unwrap();
|
||||
text(&mut d, &paper, "Real", (20.0, 20.0), "B", 0.0, 1.0, 0.0);
|
||||
text(&mut d, &paper, "Real", (20.0, 20.0), "B", (0.0, 1.0, 0.0));
|
||||
let p = pdf(&d, &paper, json!({}));
|
||||
// "AB" inside, the paper-space "B", and the glyph of "BA" left of the edge: real text. The
|
||||
// glyph the edge cuts stays a (clipped) outline.
|
||||
@@ -353,7 +353,7 @@ fn text_seen_through_a_viewport_and_on_layers() {
|
||||
#[test]
|
||||
fn text_sits_in_its_layers_marked_content() {
|
||||
let mut d = drawing();
|
||||
text(&mut d, &Space::Model, "Real", (0.0, 0.0), "AB", 0.0, 1.0, 0.0);
|
||||
text(&mut d, &Space::Model, "Real", (0.0, 0.0), "AB", (0.0, 1.0, 0.0));
|
||||
let p = pdf(&d, &Space::Model, json!({}));
|
||||
let ops = pdf_read::ops(p.content());
|
||||
let first_bt = ops.iter().position(|o| o.op == "BT").unwrap();
|
||||
|
||||
@@ -528,14 +528,14 @@ impl Family<'_> {
|
||||
if solid_line {
|
||||
out.push(vec![self.to_world(Vec2::new(x0, y)), self.to_world(Vec2::new(x1, y))]);
|
||||
} else {
|
||||
self.dash(x0, x1, y, offset, pat_len, out, work);
|
||||
self.dash((x0, x1), y, offset, pat_len, out, work);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The dashes and dots of the span `x0..x1` of the line at `y`, phase anchored at x = `offset`.
|
||||
fn dash(&self, x0: f64, x1: f64, y: f64, offset: f64, pat_len: f64, out: &mut Vec<Vec<Vec2>>, work: &mut u64) {
|
||||
fn dash(&self, (x0, x1): (f64, f64), y: f64, offset: f64, pat_len: f64, out: &mut Vec<Vec<Vec2>>, work: &mut u64) {
|
||||
let start_rep = ((x0 - offset) / pat_len).floor();
|
||||
let mut x = offset + start_rep * pat_len;
|
||||
let mut guard = 0;
|
||||
|
||||
Reference in New Issue
Block a user