mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 10:35:58 +00:00
Compare commits
729 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d4f923a3e6 | |||
| 4bdffef061 | |||
| bbb98928fb | |||
| 2aa7e3e51c | |||
| 7a7fa9a1db | |||
| 2b921df189 | |||
| 38815796a3 | |||
| 55a5f86e2e | |||
| a1c8702854 | |||
| 25fba581bd | |||
| 61f07fb294 | |||
| 47c685d471 | |||
| c7c28515ce | |||
| 01abf8af17 | |||
| ac3e6e0105 | |||
| d9310b2389 | |||
| 440fdaed13 | |||
| ce5fbf40ab | |||
| 64010a1d36 | |||
| 2baa3890bd | |||
| 3131b933c7 | |||
| c97092e385 | |||
| 44ec12cb69 | |||
| d2882754fa | |||
| 955232fbea | |||
| ae45cc6623 | |||
| a3f06a4f94 | |||
| 954e10d6f0 | |||
| 53cd9ea5aa | |||
| f3f8752153 | |||
| 29614b5e0d | |||
| e1d3d5009b | |||
| d170403841 | |||
| 8a730c6273 | |||
| 881899bee1 | |||
| 0b6991f1a9 | |||
| 9f6c340d15 | |||
| 90b3c87de3 | |||
| a59c14678f | |||
| 147b4049cf | |||
| b4a3a9baae | |||
| cc3e954f45 | |||
| 185c480319 | |||
| c5ccb9201d | |||
| 53211ab6ff | |||
| 49d9ec2be7 | |||
| 39807359f0 | |||
| ea772a0438 | |||
| 054392abbe | |||
| 28e983e191 | |||
| 841b355df1 | |||
| e831d681d3 | |||
| 30e272c44a | |||
| 28b7ae007d | |||
| 59ffd38019 | |||
| 331a8526ef | |||
| 5e54681c80 | |||
| 7bdaec8f4d | |||
| 990867cd80 | |||
| b818d7043d | |||
| f582b1f39e | |||
| 6be5be7f0c | |||
| e4cae82274 | |||
| 0d8eebbea6 | |||
| 1721cb0a4c | |||
| 3742b1a008 | |||
| 3bf29b9787 | |||
| 10bc1afec2 | |||
| 861fb8ad56 | |||
| 78b95a71b3 | |||
| f7d66d2b5b | |||
| ce6ad21106 | |||
| 71bb2c58a4 | |||
| da9960e366 | |||
| 7b8174cb88 | |||
| ef8e4d4195 | |||
| 703509bf89 | |||
| 60cb0c7342 | |||
| 71d34b9b15 | |||
| 01b5dceba1 | |||
| 887607ebb3 | |||
| ae6ced6580 | |||
| 60488a0837 | |||
| 21c5be2b25 | |||
| a9f8ccfa01 | |||
| 780c1163f9 | |||
| fa1eb57e3b | |||
| 6602ab823f | |||
| ccdb470e01 | |||
| a54d0517f7 | |||
| 7015c296ea | |||
| b73eb6c243 | |||
| 38cc3507db | |||
| 90e30126ea | |||
| e3bbd8258a | |||
| 857b13b64f | |||
| 0f8b971ef0 | |||
| 9432cdfee8 | |||
| 4a680d17ba | |||
| dadefec702 | |||
| ca4796921f | |||
| 686056e4de | |||
| d7ad69e64f | |||
| a0ec7dbdf0 | |||
| 00f7e97578 | |||
| 89f1234f28 | |||
| 6b1eb5796e | |||
| 8e2b0cd60d | |||
| 6cee7a91b9 | |||
| 927a5c3733 | |||
| c7547eb3a8 | |||
| 15e7615ce3 | |||
| 1b99e756a9 | |||
| 70b2466965 | |||
| ec1f78c6fa | |||
| 01b592e0c8 | |||
| f6e932f61a | |||
| 8ab33bb80c | |||
| e33b8cfb07 | |||
| 768dc253dd | |||
| 1d98b59883 | |||
| abea79d8ab | |||
| c4808e4715 | |||
| 6649cf4cfe | |||
| 9b431fea16 | |||
| f484457c3b | |||
| 2339510e41 | |||
| a9fa61ce77 | |||
| ac9d4e8a1d | |||
| 39163148d9 | |||
| 3ca311f922 | |||
| b978eeddcd | |||
| 2ccca96929 | |||
| 8ac4c03602 | |||
| 063f481ea0 | |||
| a025d2de9b | |||
| 3c806394b0 | |||
| 3ddd73728b | |||
| 8ec3dbc201 | |||
| ef939b52eb | |||
| c1612e9234 | |||
| 544de55baa | |||
| 961be2269c | |||
| 29a6bbdb43 | |||
| a169ad4194 | |||
| d907078434 | |||
| 073bbc3f00 | |||
| 9e5e143ac3 | |||
| 0dd8d9e5ec | |||
| 9fdf153054 | |||
| 5336007c2b | |||
| 21fc384e40 | |||
| 5d6a80ff35 | |||
| 9636188a0f | |||
| ec39a8ebb4 | |||
| 5f2de93fc6 | |||
| 450cc8b389 | |||
| 3c12c36bef | |||
| 6e67ae34b0 | |||
| 1cbebe21bf | |||
| 25a19cdefe | |||
| 7099e80059 | |||
| 12ebc80e15 | |||
| a89ea233ee | |||
| b470d46ffc | |||
| 6e98bd474e | |||
| ad23882080 | |||
| 63fb818f33 | |||
| 96509412e9 | |||
| f2ffd8d233 | |||
| 2b08e0c6a7 | |||
| a239ff50c9 | |||
| 1885fa739b | |||
| cacfbbc578 | |||
| 2ae13ba9cf | |||
| e91f5d73bd | |||
| da501178aa | |||
| 3231f049d0 | |||
| a22ea76ab6 | |||
| a735620aa8 | |||
| 059119b345 | |||
| e016c40f43 | |||
| 1fb9dadcd9 | |||
| 91a19548d3 | |||
| a277839236 | |||
| b5d931ff4c | |||
| d7a17de3d9 | |||
| a33619fd2b | |||
| e4e3c8609c | |||
| 325917f2ae | |||
| 5ad2503503 | |||
| f43198988d | |||
| 6f73bd95c4 | |||
| 9d7544929e | |||
| 3a5b38355c | |||
| fb1d5f79ad | |||
| 0aa6f15b82 | |||
| 96a8820f56 | |||
| 8f691ac257 | |||
| 50dfff716b | |||
| 47402bfa8d | |||
| 2b85a39c9c | |||
| d0fbd1f60a | |||
| 7240165c31 | |||
| 3f0e9b87fa | |||
| b1a28df0ba | |||
| 4b6bbe9d72 | |||
| 7032e6c760 | |||
| 3b31dc77d8 | |||
| 632c63f034 | |||
| 541910add2 | |||
| 6cc1acc72a | |||
| 3ddd8c7179 | |||
| 6a3d422a83 | |||
| 8fa2488a00 | |||
| 8f7a8f693c | |||
| 95788bb730 | |||
| 91ca23359e | |||
| 41700f928e | |||
| 905c02afc2 | |||
| e1b4ebfe2f | |||
| 0cb1031f2c | |||
| b6078cbe68 | |||
| 63ebc56842 | |||
| 732436fd59 | |||
| 9af354a6db | |||
| 8b35e23551 | |||
| b78df205d1 | |||
| c35be9d20e | |||
| cc313cc3fa | |||
| 66327c27aa | |||
| a98d055597 | |||
| 2eff99979a | |||
| e2186acfd7 | |||
| af35c52805 | |||
| 8702e71372 | |||
| 9e1b41f90b | |||
| 890eb79452 | |||
| d4d8be7f00 | |||
| 1c70f2c708 | |||
| ef7bd814ee | |||
| c8ea4168b1 | |||
| b9105a394e | |||
| f60f71d878 | |||
| 133de31ec9 | |||
| 59b9345750 | |||
| 67013acc28 | |||
| 4c1dee13f3 | |||
| 24134d2ed0 | |||
| cfd04c33fe | |||
| 6f88c0a0a7 | |||
| 3db45df8be | |||
| 7f9ddb31a8 | |||
| 39e9916cbc | |||
| dfe4701d22 | |||
| c324acdf29 | |||
| b6cd0d495e | |||
| dd2376923f | |||
| 8c597703f8 | |||
| ae9ff0aa71 | |||
| 0b206f2ed5 | |||
| 3b4fcd25c0 | |||
| 7d0c7e383e | |||
| 8ac8ff66b7 | |||
| 5fa7a8497c | |||
| bc93a653f6 | |||
| 33fdadcc40 | |||
| 47985ce485 | |||
| 0b8a0e08a0 | |||
| 201617ef88 | |||
| 99cd20ea20 | |||
| 143a6cb987 | |||
| d263bf1e90 | |||
| 3f0139b4d9 | |||
| 7a761d9436 | |||
| 74405f5da0 | |||
| 96181d3c41 | |||
| 47bec0a072 | |||
| 4ffc6a9017 | |||
| ff3000e10b | |||
| faf08ec1bb | |||
| 2c5a94cd1d | |||
| e58e85e3d6 | |||
| 4a0519e464 | |||
| 103b86f8f4 | |||
| b9237538e4 | |||
| b36140f6fd | |||
| e19d1d1051 | |||
| 9be549e6a1 | |||
| eeaddb56c1 | |||
| 7044a44425 | |||
| 02b8c88cf8 | |||
| f80b3ebc1f | |||
| db9897fda7 | |||
| f1ac8d3361 | |||
| 8ecfeef245 | |||
| f0a2c3f99d | |||
| 54c3b1e90e | |||
| 819461c908 | |||
| fdd103fc2a | |||
| f4dcd28de7 | |||
| e790c6428d | |||
| f43214a52f | |||
| fc3062884f | |||
| 8f4378db60 | |||
| 3afa2e874e | |||
| 5c69b88375 | |||
| e6d1893f1c | |||
| eda02a5ee7 | |||
| 1bffc3cc78 | |||
| 7df316ccce | |||
| 0710897b63 | |||
| 27d240d58c | |||
| 39e6e333c0 | |||
| 3ff9e634bc | |||
| 1db07a82f8 | |||
| a322451356 | |||
| 88c7c71761 | |||
| 20dde1a67a | |||
| 27e5a5ea8d | |||
| 54706bbb36 | |||
| d76cee7ce7 | |||
| dbec20dbbe | |||
| 1e9e80b7a7 | |||
| 5cd36d55e1 | |||
| e4ebb8c820 | |||
| 2cc1577a9d | |||
| 86390de541 | |||
| 9acdd5ec66 | |||
| d0a8be5939 | |||
| 12c2310d5e | |||
| aa1557009c | |||
| 2b2229ab31 | |||
| d3dc45681a | |||
| 98ec65d94d | |||
| cae382900a | |||
| 3348b60b54 | |||
| 68ea0f940f | |||
| fadca8440f | |||
| cd5c13e78d | |||
| 06e4d75e9c | |||
| 27a0383e1c | |||
| 7623e37e7b | |||
| 43a8e92a12 | |||
| 470cd4d761 | |||
| 35a31aa3cb | |||
| 93f8ba1a8d | |||
| 22c92d8eff | |||
| 45ee28379e | |||
| 121439114c | |||
| b10cd4b8e0 | |||
| 2aec675349 | |||
| 992b243cb6 | |||
| ca8fc27e8b | |||
| f6f2f0bc38 | |||
| 709045380e | |||
| c5a31b96dc | |||
| 295ec703fb | |||
| 80fb1768a8 | |||
| 1237203137 | |||
| 7de16a06ee | |||
| bb0d3800de | |||
| f226557ab1 | |||
| 88aaf50bf1 | |||
| d64f60bf93 | |||
| 4401e46c89 | |||
| f253d56895 | |||
| ce68658422 | |||
| c4e727871d | |||
| f0a52c36b5 | |||
| e909f3e013 | |||
| 824615e119 | |||
| 524b25b8ba | |||
| 97f5208c2c | |||
| e89b8528cd | |||
| aa161ee924 | |||
| 763f0632d1 | |||
| 99c879f9b2 | |||
| 3d5e874943 | |||
| 0106365e64 | |||
| aaecfda600 | |||
| ce8981f167 | |||
| 615e810f28 | |||
| 74bca47120 | |||
| c3670a7054 | |||
| 8ac38056c1 | |||
| b8b8d6b6a2 | |||
| aba58faeff | |||
| 74a97586a8 | |||
| d20efa730b | |||
| ae52945298 | |||
| be583dbfd7 | |||
| 5cab3f00b7 | |||
| d2bb84d6fb | |||
| 7919eaf44c | |||
| 7067258444 | |||
| dc64e105b9 | |||
| e794c6b6e4 | |||
| f3b8ae5497 | |||
| 207cb82818 | |||
| 21582b66e9 | |||
| e71d2e2844 | |||
| 48498d477a | |||
| 32ca22cf6d | |||
| 0448d3126f | |||
| 6fc8706eea | |||
| 091357ea58 | |||
| 2f7f0c679c | |||
| bc4741b05a | |||
| a93baff0be | |||
| aba3f2e1e7 | |||
| 0d3e1eac82 | |||
| 0cae7f871c | |||
| 522a8adf91 | |||
| 70beda47b1 | |||
| 4cd4ebfa50 | |||
| 0fa9c52b30 | |||
| 2480af1907 | |||
| 6537aaa0d0 | |||
| 18ac5bfd45 | |||
| 2b559115a7 | |||
| 77f1bd1c1d | |||
| 57c0e6b802 | |||
| 44e7ddfc9f | |||
| dca61e72be | |||
| 01aaf872b0 | |||
| 40426d19bd | |||
| 20aedb330d | |||
| 6d5a12789e | |||
| 14ae2c6996 | |||
| e8d0bd9c20 | |||
| 08156eb6d0 | |||
| 74cb9ca753 | |||
| 7314e9440c | |||
| 089dec39d4 | |||
| 159f9f40b3 | |||
| 789b20294e | |||
| 65b02ff7ab | |||
| 53f4d607e2 | |||
| 5a2e050f71 | |||
| 7c40ff2805 | |||
| 3099117942 | |||
| 7c14ae2ce2 | |||
| fb6a38693e | |||
| ab4f92674b | |||
| a48570cd40 | |||
| fceb07b797 | |||
| ffb0e6650b | |||
| 894b02e02f | |||
| 57921d7fcd | |||
| 40f16c36bb | |||
| 01be42ef41 | |||
| e98cd0a0e2 | |||
| cc2de29abb | |||
| 4531ab6182 | |||
| 64dfa3ca11 | |||
| d1b64fb2ff | |||
| cf8ad08ec3 | |||
| 0968a2a42d | |||
| 34c5bc74a5 | |||
| 706eafbb17 | |||
| 68263a8e02 | |||
| 57b4ce6453 | |||
| 335278e3c7 | |||
| 3dc5d6d475 | |||
| ae7a464148 | |||
| c025baddd4 | |||
| e7aadf8f40 | |||
| 176202b238 | |||
| 5a4e482e9c | |||
| 88736dc9e4 | |||
| d6dacbd6d8 | |||
| dc8b5489fc | |||
| 27b8046c6f | |||
| 5238a30bef | |||
| 9ae514b41f | |||
| 90ef5af541 | |||
| 242d8d9a7d | |||
| 6fcd6713f5 | |||
| d94ed7adb9 | |||
| 062054482b | |||
| cc425a6e5c | |||
| 5047403bb1 | |||
| 71a81514ee | |||
| 2931f71d78 | |||
| fb292f17b4 | |||
| cffbe6a72b | |||
| db2552f078 | |||
| 8f28952005 | |||
| cbd7b07767 | |||
| 7c481d281f | |||
| 2425f8b453 | |||
| a5fc58bfed | |||
| 2c2f2e5140 | |||
| 8e1005c82d | |||
| 4c922a19a9 | |||
| cc085dcd52 | |||
| e5bca0495d | |||
| a68a6ca981 | |||
| 7fe4469fc4 | |||
| 8554b2a0e4 | |||
| c7ead5658d | |||
| a889da6000 | |||
| fd0022ee15 | |||
| 30fbf80cc4 | |||
| 06c2c6087b | |||
| 9767f95c31 | |||
| 51f0a9df43 | |||
| cf0ff5159d | |||
| b11e66e606 | |||
| c2a1e7524e | |||
| c1b2eb638a | |||
| 0a108aac38 | |||
| 7b19f68368 | |||
| 9a508571fd | |||
| 69a2c57cfe | |||
| 0de308b21d | |||
| b1c82e0582 | |||
| 8b3eedd0aa | |||
| 9aedc5faed | |||
| 227e4b7794 | |||
| 59e1da2bd8 | |||
| bfae336ce1 | |||
| b6dcdd163e | |||
| 3557e3c563 | |||
| 0a014857ab | |||
| 542715de62 | |||
| 6354e472ed | |||
| e2723501ab | |||
| 82f1404518 | |||
| 2ed50ed7df | |||
| a59ec94b02 | |||
| 95a242cb67 | |||
| 460f7fa0f7 | |||
| 8291eff436 | |||
| 8a9315e7f7 | |||
| 37c118042c | |||
| 50ca3c5a61 | |||
| 816e43c5d0 | |||
| 209ef1f6c0 | |||
| 7ddfd0a389 | |||
| f6dd5c8c2e | |||
| 3696bf259e | |||
| 33ffde4d5a | |||
| 540aa0da1b | |||
| 13bb81b847 | |||
| 00659d4d12 | |||
| 58dd3a66cf | |||
| e45d7a3873 | |||
| 68c1c18e73 | |||
| 4b94403dd6 | |||
| b9e0543ef5 | |||
| fc66604b07 | |||
| 2c3f760f05 | |||
| 9f7f510843 | |||
| 2e39a45145 | |||
| 6d9703c97c | |||
| 18618bb72c | |||
| 13df957b01 | |||
| 2c4a5b6872 | |||
| ab9c676627 | |||
| 966af74030 | |||
| 585a6b4efe | |||
| 186b145be8 | |||
| ce74cebdc2 | |||
| fde43b5317 | |||
| 89457d7917 | |||
| 11be845827 | |||
| 739c9d8f5b | |||
| 5fded8864a | |||
| a8038a81df | |||
| b885929d8a | |||
| 60244c2dcd | |||
| 0d502d2634 | |||
| 22d1ca264a | |||
| 73e7636790 | |||
| 894022904f | |||
| 73713b0de2 | |||
| a51c09e063 | |||
| be01004e30 | |||
| 12dd1a17cc | |||
| 38ef4e83cc | |||
| 4a589aca8c | |||
| c302f6cbb3 | |||
| 9f8e44b216 | |||
| b1dc94da99 | |||
| bc9ab68c20 | |||
| 3e564dffb0 | |||
| 6ada9b193e | |||
| ba83c9c6fd | |||
| a1b9d92d22 | |||
| dedcd516e5 | |||
| 16f7253218 | |||
| ab69757e9b | |||
| 60fbf550b6 | |||
| 79e81613fa | |||
| cba1f888b6 | |||
| e4a9870353 | |||
| c34789dcf6 | |||
| 3092bf60b7 | |||
| 4708184430 | |||
| 54cd0d89d3 | |||
| e92a411dd7 | |||
| 6a7581deaa | |||
| e8f47435a1 | |||
| 9aa0c22097 | |||
| 4db0e94082 | |||
| d4173f612a | |||
| c30f621b98 | |||
| 52f1f0273e | |||
| 3e069a389a | |||
| ab7d133c40 | |||
| b8533c79a7 | |||
| ec7077e326 | |||
| efddcdfeeb | |||
| 699bfc12e0 | |||
| ce8c6063bd | |||
| 4eff79e57e | |||
| 52453f50fb | |||
| 0bd4531c7c | |||
| f4e2f38a27 | |||
| d62301ad01 | |||
| 782457f960 | |||
| 623679bf9d | |||
| ba418eb40d | |||
| e63bcc3808 | |||
| 24df37a653 | |||
| 3f20ea1358 | |||
| 249139ece8 | |||
| cf2b37d9c9 | |||
| 6fd5b65c1d | |||
| 043d213ce1 | |||
| a90020efb4 | |||
| eaa434e518 | |||
| 2c4da62525 | |||
| 84aa2c5b1b | |||
| 532027f7a6 | |||
| 7622329499 | |||
| f9f8f21918 | |||
| 723b84a044 | |||
| b2c79e44d8 | |||
| b5b2701591 | |||
| fa6243a283 | |||
| 5f743c46b7 | |||
| f4e6dbd1d0 | |||
| ffc289d7d8 | |||
| ac27a36f76 | |||
| da75341655 | |||
| 244f8d81cf | |||
| ae52813f2b | |||
| 8b6604dc63 | |||
| 47c5f081d4 | |||
| 157b53cb34 | |||
| 452da45fac | |||
| 924ed1b57d | |||
| 11aa1db4a4 | |||
| 0f92dda7eb | |||
| 2493b2f8c4 | |||
| 96aae9c316 | |||
| 9134b3c73e | |||
| 11de8516a1 | |||
| 68033b4357 | |||
| ed5bed8f00 | |||
| dcab94fcd7 | |||
| b86ae1b6b4 | |||
| 1fc1497af3 | |||
| ba8cca8fd7 | |||
| aa3ca73268 | |||
| be90b0ec6d | |||
| 57efe54fbd | |||
| b82fd0b769 | |||
| b5853aec6c | |||
| 3c66261621 | |||
| 5f8a58956f | |||
| ad39519794 | |||
| 98c7b65864 | |||
| 5675da8ee9 | |||
| b77d6f2df9 | |||
| 24e1e79cde | |||
| eaf1fcf436 | |||
| 5cdff8906e | |||
| dd6ab191aa | |||
| bf818a3fc0 | |||
| 69ac6ffabd | |||
| 7710849823 | |||
| 90820291dc | |||
| e1e7b4fceb | |||
| 0b25fe582c | |||
| 06bdfc1c13 | |||
| 9f47145da7 | |||
| 73056666a6 | |||
| 6d72c526bf | |||
| 059d9654c1 | |||
| d747b2f970 | |||
| 8b023a06be | |||
| 906cb69d6f | |||
| 82983c2e63 | |||
| 9e3d118f54 | |||
| 03e3bee095 | |||
| 9f1b47d689 | |||
| bdca59e7a0 | |||
| e931576723 | |||
| 539f52cc0a | |||
| 7808c5f286 | |||
| 39118d67a9 | |||
| f63a15bc68 | |||
| b1315a3de9 | |||
| 517471d0d3 | |||
| 4c4dbe4121 | |||
| e0fb4ef757 | |||
| c72062a96a | |||
| 61f8fbcbfe | |||
| 0e2b0345eb | |||
| d05b24fb5e | |||
| af6528b420 | |||
| a7ede4efab | |||
| 7f7358f918 | |||
| b9758637c8 | |||
| 709191218c | |||
| cddc4db061 | |||
| 453b257a7f | |||
| 6224363182 | |||
| 4b72c3e257 | |||
| b96d239c16 | |||
| 0b9d8600b5 | |||
| b3cf2ff2af | |||
| 1db9bf54f0 | |||
| 3faf15f08c | |||
| 9e3dc7848b |
+3
-8
@@ -1,14 +1,9 @@
|
||||
# cargo-mutants (docs/mutation-testing.md). Mutants are judged by the black-box suites that will
|
||||
# outlive the kernel backend: the reference tests (tests/oracle, through the oracle_cases test)
|
||||
# and the pull-request sample of the robustness goldens (every 8th seed, as ci.yml). The
|
||||
# kernel's own unit tests are left out on purpose: a mutant only they catch is a behaviour the
|
||||
# outlive the kernel backend: the reference tests (tests/oracle, through the oracle_cases test).
|
||||
# The kernel's own unit tests are left out on purpose: a mutant only they catch is a behaviour the
|
||||
# backend-neutral suites would miss when the kernel is swapped.
|
||||
profile = "mutants"
|
||||
additional_cargo_test_args = [
|
||||
"--test", "oracle_cases", "--lib", "--", "--include-ignored",
|
||||
"--test", "oracle_cases", "--", "--include-ignored",
|
||||
"oracle_suite",
|
||||
"robustness_goldens::seed_00", "robustness_goldens::seed_08", "robustness_goldens::seed_16",
|
||||
"robustness_goldens::seed_24", "robustness_goldens::seed_32", "robustness_goldens::seed_40",
|
||||
"robustness_goldens::seed_48", "robustness_goldens::seed_56", "robustness_goldens::seed_64",
|
||||
"robustness_goldens::seed_72", "robustness_goldens::seed_80", "robustness_goldens::seed_88",
|
||||
]
|
||||
|
||||
@@ -12,7 +12,6 @@ not (
|
||||
(package(solidcraft-engine) & (
|
||||
test(=tests::hostile_params_never_panic)
|
||||
| test(/^tests::robustness::/)
|
||||
| test(/^tests::robustness_goldens::seed_/)
|
||||
| test(=pattern_suppress_tests::enclosure_arc_extinction_keeps_a_real_sharp_inner_lip_after_resize_and_reopen)
|
||||
| test(=pattern_suppress_tests::enclosure_datums_follow_wall_height_and_board_depth_with_material_proofs)
|
||||
| test(=cmd::plastic::tests::enclosure_snap_fit_follows_resized_front_wall)
|
||||
@@ -44,13 +43,12 @@ threads-required = 12
|
||||
priority = 100
|
||||
|
||||
# The slowest tests start first, so a shard does not end with one of them running alone on a
|
||||
# core while the others sit idle (seed_81 alone takes about 20 minutes under CI load). Medians
|
||||
# from the CI test timings (the junit artifacts), slowest first; a test over ~100 s belongs here.
|
||||
# core while the others sit idle. Medians from the CI test timings (the junit artifacts), slowest
|
||||
# first; a test over ~100 s belongs here.
|
||||
[[profile.ci.overrides]]
|
||||
filter = """
|
||||
(package(solidcraft-engine) & (
|
||||
test(/^tests::robustness_goldens::seed_(81|45|20|24|95|02|57|40|18|48|32|46|85|61)$/)
|
||||
| test(/^tests::robustness::/)
|
||||
test(/^tests::robustness::/)
|
||||
| test(=tests::hostile_params_never_panic)
|
||||
| test(=cmd::clearance::tests::distances_are_exact_on_edges_and_curved_faces)
|
||||
| test(=cmd::clipboard::tests::a_pasted_coil_joins_the_pasted_body)
|
||||
@@ -92,14 +90,13 @@ threads-required = "num-cpus"
|
||||
priority = 100
|
||||
|
||||
[[profile.local-ci.overrides]]
|
||||
filter = "(package(solidcraft-engine) & test(=tests::robustness_goldens::seed_81)) | (package(solidcraft-kernel) & test(=tspline::tests::hostile_tsplines_never_panic))"
|
||||
filter = "package(solidcraft-kernel) & test(=tspline::tests::hostile_tsplines_never_panic)"
|
||||
priority = 99
|
||||
|
||||
[[profile.local-ci.overrides]]
|
||||
filter = """
|
||||
(package(solidcraft-engine) & (
|
||||
test(/^tests::robustness_goldens::seed_(81|45|20|24|95|02|57|40|18|48|32|46|85|61)$/)
|
||||
| test(/^tests::robustness::/)
|
||||
test(/^tests::robustness::/)
|
||||
| test(=tests::hostile_params_never_panic)
|
||||
| test(=cmd::clearance::tests::distances_are_exact_on_edges_and_curved_faces)
|
||||
| test(=cmd::clipboard::tests::a_pasted_coil_joins_the_pasted_body)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<!-- Title: which part of the app changes and what a user now sees or can do, clear to someone who hasn't seen your chat, the issue or the code. -->
|
||||
|
||||
## Change
|
||||
|
||||
Describe the problem and resulting behavior. Link the issue when applicable.
|
||||
Start with what this is, in plain words someone new to the project would understand: what it does and who it's for. Don't assume anyone knows the name of your tool, project or feature. Then describe the problem and resulting behavior. Link the issue when applicable.
|
||||
|
||||
Changes a default: no
|
||||
|
||||
@@ -14,6 +16,8 @@ What did you run, and what were the results? Include expected and measured value
|
||||
|
||||
## Screenshots of the behavior (when observable)
|
||||
|
||||
Put the picture or video here in the description. A file in the diff doesn't count: a maintainer has to be able to see what this is without opening the changed files.
|
||||
|
||||
For observable CAD, UI or model behavior, include actual descriptive screenshots or a short recording from a small reproduction. This includes kernel, geometry, assembly and backend effects that can be shown in the app. Coding agents should run the app and capture the evidence themselves, rather than asking a human for evidence they can produce. Show the actual failure/result, preferably before and after, with brief steps, expected/actual behavior and annotations where helpful.
|
||||
|
||||
Flowcharts and Mermaid diagrams cannot substitute for evidence of visible behavior. Use diagrams for architectural reasoning when actual screenshots cannot illustrate the point. Changes with no useful observable behavior may omit screenshot/video evidence. Exclude private designs and sensitive details; attach media or link to an accessible recording rather than committing generated review artifacts.
|
||||
|
||||
+50
-53
@@ -33,6 +33,8 @@
|
||||
# `ci` job still runs and reports the commit's real result (see the `ci` job): the merge queue
|
||||
# reads `ci` from the newest run of a commit, so a run without it would block a PR that passed.
|
||||
# Cross-compilation is opt-in through Run workflow; release builds remain in release.yml.
|
||||
# The release gate (release-gate.yml) calls this workflow (workflow_call) as a Run workflow with
|
||||
# cross-compilation on.
|
||||
# Every Linux job runs on Blacksmith's Ubuntu 24.04 runners (blacksmith-<N>vcpu-ubuntu-2404), sized
|
||||
# per job: 32 vCPU for the tests, 16 for Clippy, the GPU path tests, the archive build (nightly) and the merge queue's, 8 for compiles, doc and reference tests, 4 for the light checks, 2 for Select checks and ci.
|
||||
# The Windows shader job runs on Blacksmith's Windows Server 2025 runners (blacksmith-8vcpu-windows-2025);
|
||||
@@ -50,10 +52,14 @@ on:
|
||||
# only (other labels are skipped, see the top of this file).
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review, labeled]
|
||||
# The kernel-smlib branch (ADR 0002) runs no CI for now (owner, 2026-10-21). Drop this
|
||||
# line before the branch merges back into main.
|
||||
branches-ignore: [kernel-smlib]
|
||||
# The merge queue: each queued group runs the full suite before it lands on main.
|
||||
merge_group:
|
||||
push:
|
||||
branches: [main]
|
||||
# develop: the SMLib kernel's trunk until it merges into main (its PRs land there).
|
||||
branches: [main, develop]
|
||||
schedule:
|
||||
- cron: "17 6 * * *"
|
||||
workflow_dispatch:
|
||||
@@ -62,6 +68,14 @@ on:
|
||||
description: "Also check WebAssembly and RISC-V (before a release)"
|
||||
type: boolean
|
||||
default: false
|
||||
# The release gate (release-gate.yml): the event stays the caller's (workflow_dispatch), so a
|
||||
# call runs exactly what Run workflow runs.
|
||||
workflow_call:
|
||||
inputs:
|
||||
cross_compile:
|
||||
description: "Also check WebAssembly and RISC-V (before a release)"
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -117,7 +131,6 @@ jobs:
|
||||
outputs:
|
||||
native: ${{ steps.paths.outputs.native }}
|
||||
kernel: ${{ steps.paths.outputs.kernel }}
|
||||
goldens: ${{ steps.paths.outputs.goldens }}
|
||||
windows: ${{ steps.paths.outputs.windows }}
|
||||
# The GPU path tests run (see the rule where it is computed). The job's `if` and the `ci` job's check both read this, so they cannot disagree.
|
||||
gpu: ${{ steps.paths.outputs.gpu }}
|
||||
@@ -156,7 +169,6 @@ jobs:
|
||||
run: |
|
||||
native=false
|
||||
kernel=false
|
||||
goldens=false
|
||||
windows=false
|
||||
gpu_paths=false
|
||||
deps=false
|
||||
@@ -238,12 +250,12 @@ jobs:
|
||||
if [ "${tested:-0}" -gt 0 ]; then
|
||||
echo "The merge queue already tested $PUSH_SHA: repository checks only."
|
||||
elif tested=$(curl -fsS -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
|
||||
"https://api.github.com/repos/$REPO/actions/workflows/ci.yml/runs?event=push&branch=main&status=success&per_page=1" \
|
||||
"https://api.github.com/repos/$REPO/actions/workflows/ci.yml/runs?event=push&branch=$GITHUB_REF_NAME&status=success&per_page=1" \
|
||||
| grep -o '"head_sha": *"[0-9a-f]*"' | head -n 1 | grep -o '[0-9a-f]\{40\}') \
|
||||
&& [ -n "$tested" ] && git merge-base --is-ancestor "$tested" "$PUSH_SHA" \
|
||||
&& git diff --no-renames --name-only -z "$tested" "$PUSH_SHA" > "$RUNNER_TEMP/ci-paths" \
|
||||
&& select_packages "$RUNNER_TEMP/ci-paths" && [ "$full" != true ] && [ -z "$packages" ]; then
|
||||
# Nothing a build reads changed since the last push to main whose run passed (the
|
||||
# Nothing a build reads changed since the last push to this branch whose run passed (the
|
||||
# push before may never have run: a newer push cancels a waiting one).
|
||||
echo "$tested..$PUSH_SHA changes no code: repository checks only."
|
||||
else
|
||||
@@ -282,13 +294,6 @@ jobs:
|
||||
git diff --no-renames --name-only -z "$BASE_SHA...$HEAD_SHA" > "$RUNNER_TEMP/ci-paths"
|
||||
while IFS= read -r -d '' path; do
|
||||
case "$path" in crates/kernel/*|vendor/truck-*) kernel=true ;; esac
|
||||
# What a pull request re-checks the goldens for: the kernel, truck and the goldens
|
||||
# themselves (a re-record is checked before it is queued). The goldens are frozen, so
|
||||
# on any other change they would only re-check unchanged results; the merge queue,
|
||||
# main and the nightly run every seed before anything lands.
|
||||
case "$path" in
|
||||
crates/kernel/*|vendor/truck-*|tests/robustness/*) goldens=true ;;
|
||||
esac
|
||||
done < "$RUNNER_TEMP/ci-paths"
|
||||
# Computed as for a merge group, then turned off below (the fast lane).
|
||||
windows_paths "$RUNNER_TEMP/ci-paths" "$BASE_SHA...$HEAD_SHA"
|
||||
@@ -300,10 +305,6 @@ jobs:
|
||||
# selected means no code changed.
|
||||
select_packages "$RUNNER_TEMP/ci-paths"
|
||||
if [ "$full" = true ] || [ -n "$packages" ]; then native=true; fi
|
||||
# The goldens are solidcraft-engine tests: its tests must be in the archive.
|
||||
if [ "$goldens" = true ] && [ "$full" != true ]; then
|
||||
case " $packages " in *" solidcraft-engine "*) ;; *) packages="$packages solidcraft-engine" ;; esac
|
||||
fi
|
||||
fi
|
||||
# The reference tests replay recipes through solidcraft-cli: needed when it, or anything
|
||||
# it depends on, is selected.
|
||||
@@ -318,13 +319,12 @@ jobs:
|
||||
doctests=$native
|
||||
if [ "$EVENT_NAME" = pull_request ]; then windows=false; oracle=false; doctests=false; fi
|
||||
# Until a maintainer adds approved-for-ci, a pull request gets a quick compile instead of
|
||||
# Clippy, the tests, the doc tests and the goldens.
|
||||
# Clippy, the tests and the doc tests.
|
||||
quick=false
|
||||
if [ "$EVENT_NAME" = pull_request ] && [ "$APPROVED" != true ]; then
|
||||
quick=$native
|
||||
native=false
|
||||
oracle=false
|
||||
goldens=false
|
||||
fi
|
||||
# A push that only merges main into the change (Merge branch 'main', Update branch) when
|
||||
# the change itself already passed: nothing new to test here, and main tests the merged
|
||||
@@ -337,7 +337,6 @@ jobs:
|
||||
if [ -n "$carried" ]; then
|
||||
echo "Only a merge of main into a change that passed in $carried: repository checks only."
|
||||
native=false
|
||||
goldens=false
|
||||
fi
|
||||
fi
|
||||
# The GPU path tests (their own job, about 56 runner-minutes) are for render changes only:
|
||||
@@ -363,7 +362,6 @@ jobs:
|
||||
echo "native=$native"
|
||||
echo "gpu=$gpu"
|
||||
echo "kernel=$kernel"
|
||||
echo "goldens=$goldens"
|
||||
echo "windows=$windows"
|
||||
echo "oracle=$oracle"
|
||||
echo "doctests=$doctests"
|
||||
@@ -389,7 +387,7 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: repo-checks
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' }}
|
||||
- name: Format
|
||||
run: cargo fmt --all -- --check
|
||||
- name: Asset attribution
|
||||
@@ -409,6 +407,10 @@ jobs:
|
||||
# New UI text must reach every complete catalog; report it here, before review (#8542).
|
||||
- name: Translations in the complete catalogs
|
||||
run: cargo xtask i18n --check
|
||||
- name: Model decisions use exact geometry, not meshes
|
||||
run: |
|
||||
cargo xtask mesh-decisions
|
||||
cargo test -p xtask mesh_decisions --locked
|
||||
- name: Agent router and skills cite real files
|
||||
run: |
|
||||
cargo xtask skills
|
||||
@@ -439,7 +441,7 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: quick-compile
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' }}
|
||||
- name: Cargo check
|
||||
run: cargo check --workspace --all-targets --locked
|
||||
|
||||
@@ -458,7 +460,7 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: native-clippy
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' }}
|
||||
- name: Clippy
|
||||
env:
|
||||
FULL: ${{ needs.changes.outputs.full }}
|
||||
@@ -501,7 +503,7 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: native-tests
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' }}
|
||||
- name: Set up compiler cache
|
||||
uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
|
||||
with:
|
||||
@@ -572,7 +574,7 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: native-tests
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' }}
|
||||
- name: Set up compiler cache
|
||||
uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
|
||||
with:
|
||||
@@ -583,13 +585,8 @@ jobs:
|
||||
- name: Run tests
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
GOLDENS: ${{ needs.changes.outputs.goldens }}
|
||||
FULL: ${{ needs.changes.outputs.full }}
|
||||
PACKAGES: ${{ needs.changes.outputs.packages }}
|
||||
# The robustness golden seeds a pull request skips: each takes one to twenty minutes under
|
||||
# CI load, together about four fifths of the goldens' time. Pushes to main, the merge
|
||||
# queue and the nightly run every seed.
|
||||
SLOW_SEEDS: 81|45|20|24|95|02|57|40|18|48|32|46|85|61|77|34|14
|
||||
run: |
|
||||
# Pull requests skip the heavy tests (default-filter in .config/nextest.toml); `default()`
|
||||
# below is that filter, so a pull request still runs only what it ran before.
|
||||
@@ -603,29 +600,23 @@ jobs:
|
||||
# The boolean property tests draw new random inputs every run; they run only in the
|
||||
# nightly property-tests job, so a known kernel bug cannot turn main red at random.
|
||||
base='not (package(solidcraft-kernel) & binary(boolean_props))'
|
||||
elif [ "$GOLDENS" = true ]; then
|
||||
# A pull request that can change a robustness golden (tests/robustness/goldens.json)
|
||||
# also checks every seed but the slow ones, in the same run so they share its cores.
|
||||
base="(default() & not (binary(action_matrix) & test(=action_matrix_sample))) | (package(solidcraft-engine) & test(/^tests::robustness_goldens::/) & not test(/^tests::robustness_goldens::seed_($SLOW_SEEDS)\$/))"
|
||||
fi
|
||||
junit=${CARGO_TARGET_DIR:-target}/nextest/ci/junit.xml
|
||||
rm -f "$junit"
|
||||
# Pull requests test the affected packages (scripts/ci/affected.py).
|
||||
scope=(--workspace)
|
||||
if [ "$FULL" != true ]; then scope=(); for p in $PACKAGES; do scope+=(-p "$p"); done; fi
|
||||
status=0
|
||||
# The GPU path tracer's tests run in their own job (GPU path tests): one at a time, they
|
||||
# took about 36 of this job's 45 minutes.
|
||||
# A failed test does not fail the step by itself: the tests known to fail today
|
||||
# (tests/KNOWN_FAILING_TESTS) may fail, any other fails it, and so does a listed test
|
||||
# that passes. nextest's exit code 100 is "tests failed"; any other is its own error
|
||||
# (a build error, a bad filter) and fails the step at once.
|
||||
status=0
|
||||
cargo nextest run "${scope[@]}" --cargo-profile ci --locked --profile ci --no-tests=pass \
|
||||
--ignore-default-filter -E "($base) & not (package(=solidcraft-gpu) & test(/^path::/))" || status=1
|
||||
if [ "$status" != 0 ]; then
|
||||
# A failed golden gets the hint to re-record it (junit.xml lists the failures).
|
||||
if [ -f "$junit" ] && tr '\n' ' ' < "$junit" | grep -q '<testcase name="tests::robustness_goldens::[^"]*"[^>]*>[[:space:]]*<failure'; then
|
||||
echo "::error::A robustness golden changed. If the new result is intended, re-record it in this pull request and say why:"
|
||||
echo "::error::ROBUSTNESS_GOLDENS_BLESS=\"why it changed\" cargo test --profile ci -p solidcraft-engine --lib robustness_goldens:: (see docs/robustness.md)"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
--ignore-default-filter -E "($base) & not (package(=solidcraft-gpu) & test(/^path::/))" || status=$?
|
||||
if [ "$status" -ne 0 ] && [ "$status" -ne 100 ]; then exit "$status"; fi
|
||||
cargo xtask known-failing --junit "$junit"
|
||||
- name: Compiler cache statistics
|
||||
if: always()
|
||||
run: sccache --show-stats
|
||||
@@ -699,7 +690,7 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: native-gpu
|
||||
save-if: ${{ github.ref == 'refs/heads/main' && matrix.shard == 1 }}
|
||||
save-if: ${{ (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop') && matrix.shard == 1 }}
|
||||
- name: Set up compiler cache
|
||||
uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
|
||||
with:
|
||||
@@ -714,8 +705,14 @@ jobs:
|
||||
# None of these is in the default filter (the heavy tests), so pull requests, the merge
|
||||
# queue, main and nightly all run every one of them, as before. `count` deals the tests
|
||||
# out in turn (27 each); `hash` split them 41/40/27 across three shards.
|
||||
# Graded against tests/KNOWN_FAILING_TESTS, as in Native Linux tests.
|
||||
junit=${CARGO_TARGET_DIR:-target}/nextest/ci/junit.xml
|
||||
rm -f "$junit"
|
||||
status=0
|
||||
cargo nextest run -p solidcraft-gpu --cargo-profile ci --locked --profile ci --no-tests=pass \
|
||||
--ignore-default-filter -E 'test(/^path::/)' --partition "count:$SHARD/4"
|
||||
--ignore-default-filter -E 'test(/^path::/)' --partition "count:$SHARD/4" || status=$?
|
||||
if [ "$status" -ne 0 ] && [ "$status" -ne 100 ]; then exit "$status"; fi
|
||||
cargo xtask known-failing --junit "$junit"
|
||||
- name: Compiler cache statistics
|
||||
if: always()
|
||||
run: sccache --show-stats
|
||||
@@ -849,7 +846,7 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: doc-tests
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' }}
|
||||
- name: Set up compiler cache
|
||||
uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
|
||||
with:
|
||||
@@ -907,7 +904,7 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: oracle
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' }}
|
||||
- name: Set up compiler cache
|
||||
uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
|
||||
with:
|
||||
@@ -946,7 +943,7 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: windows-fxc
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' }}
|
||||
# One test at a time: WARP's shader JIT crashes now and then (a null read in
|
||||
# d3d10warp.dll's JIT optimizer, about 1 run in 60) when several devices compile compute
|
||||
# pipelines at once, which the parallel tests do. Minidumps showed it in WARP itself, not
|
||||
@@ -970,7 +967,7 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: windows-startup
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' }}
|
||||
- name: Backend order and probing on Windows
|
||||
run: cargo test -p solidcraft --profile ci --locked -- startup::tests native_crash::tests native_frame::tests
|
||||
- name: Build the app
|
||||
@@ -1107,7 +1104,7 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: wasm
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' }}
|
||||
- run: cargo xtask wasm
|
||||
|
||||
riscv64-gates:
|
||||
@@ -1129,7 +1126,7 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: riscv64
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' }}
|
||||
- name: Install cross-compilation dependencies
|
||||
run: |
|
||||
# Ubuntu's regular archive serves amd64; RISC-V packages live in ports.
|
||||
@@ -1145,7 +1142,7 @@ jobs:
|
||||
EOF
|
||||
sudo dpkg --add-architecture riscv64
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc-riscv64-linux-gnu libasound2-dev:riscv64 libxkbcommon-dev:riscv64 \
|
||||
sudo apt-get install -y gcc-riscv64-linux-gnu g++-riscv64-linux-gnu libasound2-dev:riscv64 libxkbcommon-dev:riscv64 \
|
||||
libwayland-dev:riscv64 libx11-dev:riscv64 libxrandr-dev:riscv64 libxi-dev:riscv64 libgl1-mesa-dev:riscv64
|
||||
- name: Cargo check on RISC-V
|
||||
env:
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
# The fillet rig as a regression gate: a pull request that touches fillet code (the paths below)
|
||||
# runs every case in tests/kernel-cases/fillets/ through one release build of
|
||||
# solidcraft-cli, and fails on a regression (a case that fails and
|
||||
# tests/kernel-cases/fillets/KNOWN_FAILING does not list) or on a listed case that now passes
|
||||
# (it is fixed: remove its line, so the list only shrinks). See tests/kernel-cases/README.md.
|
||||
#
|
||||
# GitHub filters a workflow by whole files, so the fillet parts of crates/doc/src/eval*.rs and
|
||||
# crates/engine/src/cmd/features.rs select it whenever those files change. Pushes to main and
|
||||
# develop that touch the same paths run it too and save the compiler cache (a Blacksmith sticky
|
||||
# disk) that pull requests then mount read-only.
|
||||
name: Fillet rig
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
paths:
|
||||
- "crates/kernel/src/blend*.rs"
|
||||
- "crates/kernel/src/fillet*.rs"
|
||||
- "crates/kernel/src/*corner*.rs"
|
||||
- "crates/kernel/src/continuity*.rs"
|
||||
- "crates/smlib/**"
|
||||
- "crates/doc/src/eval*.rs"
|
||||
- "crates/engine/src/cmd/features.rs"
|
||||
- "tests/kernel-cases/fillets/**"
|
||||
# The rig itself: the grading, the runner and this workflow.
|
||||
- "apps/solidcraft-cli/src/kernel_cases.rs"
|
||||
- "xtask/src/kernel_cases.rs"
|
||||
- ".github/workflows/fillet-rig.yml"
|
||||
push:
|
||||
branches: [main, develop]
|
||||
# The same paths as pull_request.
|
||||
paths:
|
||||
- "crates/kernel/src/blend*.rs"
|
||||
- "crates/kernel/src/fillet*.rs"
|
||||
- "crates/kernel/src/*corner*.rs"
|
||||
- "crates/kernel/src/continuity*.rs"
|
||||
- "crates/smlib/**"
|
||||
- "crates/doc/src/eval*.rs"
|
||||
- "crates/engine/src/cmd/features.rs"
|
||||
- "tests/kernel-cases/fillets/**"
|
||||
# The rig itself: the grading, the runner and this workflow.
|
||||
- "apps/solidcraft-cli/src/kernel_cases.rs"
|
||||
- "xtask/src/kernel_cases.rs"
|
||||
- ".github/workflows/fillet-rig.yml"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: fillet-rig-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
fillets:
|
||||
name: Fillet rig
|
||||
# Draft pull requests run no CI, as in ci.yml.
|
||||
if: github.event_name != 'pull_request' || !github.event.pull_request.draft
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CARGO_INCREMENTAL: "0"
|
||||
RUSTC_WRAPPER: sccache
|
||||
SCCACHE_DIR: /home/runner/.cache/sccache
|
||||
SCCACHE_CACHE_SIZE: 10G
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Size parallel compiles to the job's memory
|
||||
run: scripts/ci/build-jobs.sh
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
# Only main and develop commit the cache; pull requests and forks mount a clone and never
|
||||
# write back.
|
||||
- name: Mount the compiler cache
|
||||
uses: useblacksmith/stickydisk@3f6be1451e3cad893b778bc93495c80452a5da26 # v1.7.2
|
||||
continue-on-error: true
|
||||
with:
|
||||
key: ${{ github.repository }}-sccache-fillet-rig
|
||||
path: /home/runner/.cache/sccache
|
||||
commit: ${{ (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop') && 'on-change' || 'false' }}
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: fillet-rig
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' }}
|
||||
- name: Set up compiler cache
|
||||
uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
|
||||
with:
|
||||
version: v0.16.0
|
||||
- name: Build the CLI (release)
|
||||
run: cargo build --release --locked -p solidcraft-cli
|
||||
- name: Run the fillet cases against the known-failing list
|
||||
run: >-
|
||||
cargo run -q --locked -p xtask -- kernel-cases --area fillets
|
||||
--cli target/release/solidcraft-cli --out "$RUNNER_TEMP/fillet-rig"
|
||||
--known-failing tests/kernel-cases/fillets/KNOWN_FAILING
|
||||
- name: Keep the results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: fillet-rig-results
|
||||
path: ${{ runner.temp }}/fillet-rig/results.json
|
||||
if-no-files-found: ignore
|
||||
@@ -0,0 +1,238 @@
|
||||
# The kernel rig and the complexity ladder as a regression gate. A plan job maps a pull request's
|
||||
# changed files to the rig areas they can affect (scripts/ci/kernel_rig_plan.py): an area runs
|
||||
# when a file its tests/kernel-cases/<area>/PATHS lists changes, or one of its cases; every area
|
||||
# runs when the shared core in tests/kernel-cases/PATHS-ALL changes, and when the solidcore pin in
|
||||
# the root Cargo.toml and Cargo.lock moves (the plan reads their diff); the ladder runs whenever any
|
||||
# area does. One job then builds solidcraft-cli once (release), checks that every
|
||||
# registry command has a case or an exemption (tests/kernel-cases/EXEMPT), and hands the CLI to a
|
||||
# matrix of shards as an artifact, one per selected area plus the ladder, in parallel, so the
|
||||
# wall time is the build plus the slowest shard. See tests/kernel-cases/README.md.
|
||||
#
|
||||
# - An area fails on a regression (a case that fails and its tests/kernel-cases/<area>/KNOWN_FAILING
|
||||
# does not list) or on a listed case that passes (it is fixed: remove its line in the same pull
|
||||
# request, so the list only shrinks).
|
||||
# - The ladder fails on any case whose status or counts differ from
|
||||
# tests/kernel-cases/ladder/golden.json; a right change is re-recorded (`--record "why"`).
|
||||
#
|
||||
# The trigger paths are whole folders that hold every area's PATHS (a self-test,
|
||||
# scripts/ci/test_kernel_rig_plan.py, checks they do), so a pull request that touches none of
|
||||
# them (the UI, docs, packaging) starts no runner at all, and a new area needs no edit here: the
|
||||
# plan finds the areas from the folders. Pushes to main and develop on the same paths, merge groups
|
||||
# (once the queue is on), the release gate (release-gate.yml, workflow_call) and a run by hand run
|
||||
# every area; those pushes also commit the compiler cache (a Blacksmith sticky disk) that every
|
||||
# other run mounts read-only.
|
||||
name: Kernel rig
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
paths:
|
||||
- "crates/kernel/**"
|
||||
- "crates/geom/**"
|
||||
- "crates/smlib/**"
|
||||
- "crates/step/**"
|
||||
- "crates/io/src/**"
|
||||
- "crates/doc/src/**"
|
||||
- "crates/engine/src/**"
|
||||
- "tests/kernel-cases/**"
|
||||
# The solidcore pin (the plan runs every area only when its lines change).
|
||||
- "Cargo.toml"
|
||||
- "Cargo.lock"
|
||||
# The rig itself: the grading, the runner, the selection and this workflow.
|
||||
- "apps/solidcraft-cli/src/kernel_cases.rs"
|
||||
- "xtask/src/kernel_cases.rs"
|
||||
- "xtask/src/kernel_ladder.rs"
|
||||
- "scripts/ci/kernel_rig_plan.py"
|
||||
- "scripts/ci/test_kernel_rig_plan.py"
|
||||
- ".github/workflows/kernel-rig.yml"
|
||||
push:
|
||||
branches: [main, develop]
|
||||
# The same paths as pull_request.
|
||||
paths:
|
||||
- "crates/kernel/**"
|
||||
- "crates/geom/**"
|
||||
- "crates/smlib/**"
|
||||
- "crates/step/**"
|
||||
- "crates/io/src/**"
|
||||
- "crates/doc/src/**"
|
||||
- "crates/engine/src/**"
|
||||
- "tests/kernel-cases/**"
|
||||
# The solidcore pin (the plan runs every area only when its lines change).
|
||||
- "Cargo.toml"
|
||||
- "Cargo.lock"
|
||||
# The rig itself: the grading, the runner, the selection and this workflow.
|
||||
- "apps/solidcraft-cli/src/kernel_cases.rs"
|
||||
- "xtask/src/kernel_cases.rs"
|
||||
- "xtask/src/kernel_ladder.rs"
|
||||
- "scripts/ci/kernel_rig_plan.py"
|
||||
- "scripts/ci/test_kernel_rig_plan.py"
|
||||
- ".github/workflows/kernel-rig.yml"
|
||||
merge_group:
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
# A new push to a pull request replaces its old run.
|
||||
group: kernel-rig-${{ github.event_name }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
plan:
|
||||
name: Select the rig areas
|
||||
# Draft pull requests run no CI, as in ci.yml.
|
||||
if: github.event_name != 'pull_request' || !github.event.pull_request.draft
|
||||
runs-on: blacksmith-2vcpu-ubuntu-2404
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
# The shard matrix (JSON list of areas and the ladder; [] when nothing is selected).
|
||||
shards: ${{ steps.plan.outputs.shards }}
|
||||
# Whether the build job, and with it the coverage check, runs.
|
||||
build: ${{ steps.plan.outputs.build }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
# A pull request's diff needs its base's history.
|
||||
fetch-depth: ${{ github.event_name == 'pull_request' && '0' || '1' }}
|
||||
- name: Selection self-test
|
||||
run: python3 -I scripts/ci/test_kernel_rig_plan.py
|
||||
- id: plan
|
||||
name: Map the changed files to areas
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
run: |
|
||||
# Pull requests run the areas their diff reaches; pushes, merge groups, the release
|
||||
# gate and a run by hand run every area.
|
||||
if [ "$EVENT_NAME" = pull_request ]; then
|
||||
git diff --no-renames --name-only -z "$BASE_SHA...$HEAD_SHA" > "$RUNNER_TEMP/rig-paths"
|
||||
git diff --no-renames "$BASE_SHA...$HEAD_SHA" -- Cargo.toml Cargo.lock > "$RUNNER_TEMP/rig-pin.diff"
|
||||
python3 -I scripts/ci/kernel_rig_plan.py --paths "$RUNNER_TEMP/rig-paths" --pin-diff "$RUNNER_TEMP/rig-pin.diff" \
|
||||
--github-output "$GITHUB_OUTPUT"
|
||||
else
|
||||
python3 -I scripts/ci/kernel_rig_plan.py --all --github-output "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
build:
|
||||
name: Build the CLI
|
||||
needs: plan
|
||||
if: needs.plan.outputs.build == 'true'
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CARGO_INCREMENTAL: "0"
|
||||
RUSTC_WRAPPER: sccache
|
||||
SCCACHE_DIR: /home/runner/.cache/sccache
|
||||
SCCACHE_CACHE_SIZE: 10G
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Size parallel compiles to the job's memory
|
||||
run: scripts/ci/build-jobs.sh
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
# Only pushes to main and develop commit the cache; pull requests, merge groups and forks
|
||||
# mount a clone and never write back.
|
||||
- name: Mount the compiler cache
|
||||
uses: useblacksmith/stickydisk@3f6be1451e3cad893b778bc93495c80452a5da26 # v1.7.2
|
||||
continue-on-error: true
|
||||
with:
|
||||
key: ${{ github.repository }}-sccache-kernel-rig
|
||||
path: /home/runner/.cache/sccache
|
||||
commit: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop') && 'on-change' || 'false' }}
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: kernel-rig
|
||||
save-if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop') }}
|
||||
- name: Set up compiler cache
|
||||
uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
|
||||
with:
|
||||
version: v0.16.0
|
||||
- name: Build the CLI (release) and the rig's runner
|
||||
run: |
|
||||
cargo build --release --locked -p solidcraft-cli
|
||||
cargo build --locked -p xtask
|
||||
# A tar keeps the executable bits, which an artifact drops. xtask finds the repository from
|
||||
# the path it was built in (CARGO_MANIFEST_DIR), which is the same checkout path in every
|
||||
# job of this workflow.
|
||||
# Every registry command is used by a case or listed in tests/kernel-cases/EXEMPT.
|
||||
- name: Every command has a case
|
||||
run: target/debug/xtask kernel-cases --coverage --cli target/release/solidcraft-cli
|
||||
# A coverage-only run (the registry or the cases changed, no area selected) runs no shard.
|
||||
- name: Pack the binaries
|
||||
if: needs.plan.outputs.shards != '[]'
|
||||
run: tar -cf "$RUNNER_TEMP/kernel-rig-bin.tar" -C target release/solidcraft-cli debug/xtask
|
||||
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
if: needs.plan.outputs.shards != '[]'
|
||||
with:
|
||||
name: kernel-rig-bin
|
||||
path: ${{ runner.temp }}/kernel-rig-bin.tar
|
||||
retention-days: 1
|
||||
- name: Compiler cache statistics
|
||||
if: always()
|
||||
run: sccache --show-stats
|
||||
|
||||
shard:
|
||||
name: Kernel rig (${{ matrix.shard }})
|
||||
needs: [plan, build]
|
||||
if: needs.plan.outputs.shards != '[]'
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# The areas of tests/kernel-cases/ the plan selected, and the ladder. On the build box
|
||||
# (aarch64, 4 cases at a time) the slowest areas took about 1 minute (booleans, 212 s of
|
||||
# cases with one of 84 s; threads, 161 s) and the ladder, one case at a time, 92 s: no area
|
||||
# needs splitting.
|
||||
shard: ${{ fromJSON(needs.plan.outputs.shards) }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
||||
with:
|
||||
name: kernel-rig-bin
|
||||
path: ${{ runner.temp }}
|
||||
- name: Unpack the binaries
|
||||
run: mkdir -p target && tar -xf "$RUNNER_TEMP/kernel-rig-bin.tar" -C target
|
||||
- name: Run the cases against the known-failing list (the ladder against its golden)
|
||||
env:
|
||||
SHARD: ${{ matrix.shard }}
|
||||
run: |
|
||||
if [ "$SHARD" = ladder ]; then
|
||||
set -- --ladder --gate
|
||||
else
|
||||
set -- --area "$SHARD" --known-failing "tests/kernel-cases/$SHARD/KNOWN_FAILING"
|
||||
fi
|
||||
target/debug/xtask kernel-cases "$@" --cli target/release/solidcraft-cli --out "$RUNNER_TEMP/kernel-rig"
|
||||
- name: Keep the results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: kernel-rig-results-${{ matrix.shard }}
|
||||
path: ${{ runner.temp }}/kernel-rig/**/results.json
|
||||
if-no-files-found: ignore
|
||||
|
||||
# One check for the whole gate, for branch protection and the merge queue.
|
||||
kernel-rig:
|
||||
name: kernel-rig
|
||||
needs: [plan, build, shard]
|
||||
if: always() && (github.event_name != 'pull_request' || !github.event.pull_request.draft)
|
||||
runs-on: blacksmith-2vcpu-ubuntu-2404
|
||||
steps:
|
||||
# A skipped build or shard counts as passed only when the plan selected none.
|
||||
- name: Every selected job passed
|
||||
env:
|
||||
PLAN: ${{ needs.plan.result }}
|
||||
WANT_BUILD: ${{ needs.plan.outputs.build }}
|
||||
WANT_SHARDS: ${{ needs.plan.outputs.shards }}
|
||||
BUILD: ${{ needs.build.result }}
|
||||
SHARDS: ${{ needs.shard.result }}
|
||||
run: |
|
||||
echo "plan=$PLAN build=$BUILD shards=$SHARDS selected=$WANT_SHARDS"
|
||||
test "$PLAN" = success
|
||||
if [ "$WANT_BUILD" = true ]; then test "$BUILD" = success; else test "$BUILD" = skipped; fi
|
||||
if [ "$WANT_SHARDS" != '[]' ]; then test "$SHARDS" = success; else test "$SHARDS" = skipped; fi
|
||||
@@ -1,6 +1,6 @@
|
||||
# Two quick checks on every pull request (CONTRIBUTING.md, "Sending a change"): no second open PR
|
||||
# for an issue a lower-numbered one already closes, and the "Changes a default:" line is filled in.
|
||||
# Neither is a required status check. The logic is in scripts/pr-checks/pr-checks.js (tested in ci.yml).
|
||||
# Three quick checks on every pull request (CONTRIBUTING.md, "Sending a change"): no second open PR
|
||||
# for an issue a lower-numbered one already closes, the "Changes a default:" line is filled in, and
|
||||
# a PR adding to the showcase shows a picture. None is a required status check. The logic is in scripts/pr-checks/pr-checks.js (tested in ci.yml).
|
||||
name: PR checks
|
||||
|
||||
on:
|
||||
@@ -48,6 +48,21 @@ jobs:
|
||||
const r = await checks.checkDefaults({ github, context, core });
|
||||
core.info(JSON.stringify(r));
|
||||
|
||||
picture:
|
||||
name: Picture check
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
sparse-checkout: scripts/pr-checks
|
||||
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const checks = require('./scripts/pr-checks/pr-checks.js');
|
||||
const r = await checks.checkPicture({ github, context, core });
|
||||
core.info(JSON.stringify(r));
|
||||
|
||||
# Labelling needs write access, which a fork PR's token never has, so it is its own job and best
|
||||
# effort: a fork PR just doesn't get the `changes-default` label from here.
|
||||
label:
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
# The release gate: everything, in full and in parallel, on one branch's head, before that branch
|
||||
# replaces main as a release (develop, rebased onto main). Run it by hand:
|
||||
# gh workflow run release-gate.yml --ref develop
|
||||
# It calls the workflows that own each check instead of copying their jobs:
|
||||
# - ci.yml as Run workflow with cross-compilation: every native suite in full (not scoped), the
|
||||
# reference tests, the doc tests, the GPU path tests, the action matrix (all 16 shards) and its
|
||||
# determinism check, the property tests, the repository checks (web-offline among them),
|
||||
# WebAssembly and RISC-V, Windows D3D12 FXC and Windows app startup;
|
||||
# - kernel-rig.yml: every rig area against its KNOWN_FAILING and the ladder against its golden;
|
||||
# - release.yml as a dry run (draft false, packages_only): the Linux, Flatpak, Snap, FreeBSD,
|
||||
# macOS, Windows and web packages with their smoke tests and package checks, unsigned (no
|
||||
# secrets are passed, so no signing secret or SignPath quota is used) and without the release
|
||||
# job (checksums, update signing, publishing).
|
||||
# The gate passes only when all three pass. Before main is replaced, tag the old main
|
||||
# (main-before-develop-<date>) and push the tag.
|
||||
name: Release gate
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: release-gate-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
name: CI
|
||||
uses: ./.github/workflows/ci.yml
|
||||
permissions:
|
||||
contents: read
|
||||
# Select checks reads this commit's other runs.
|
||||
actions: read
|
||||
with:
|
||||
cross_compile: true
|
||||
|
||||
kernel-rig:
|
||||
name: Kernel rig
|
||||
uses: ./.github/workflows/kernel-rig.yml
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
packages:
|
||||
name: Packages
|
||||
uses: ./.github/workflows/release.yml
|
||||
# No secrets: the signing steps skip without them. GitHub checks a called workflow's job
|
||||
# permissions before the run, skipped jobs included, so the release job's contents: write must
|
||||
# be granted here; packages_only skips that job, and no step that runs writes to the
|
||||
# repository. actions: read is the Windows job's.
|
||||
permissions:
|
||||
contents: write
|
||||
actions: read
|
||||
with:
|
||||
draft: false
|
||||
packages_only: true
|
||||
|
||||
release-gate:
|
||||
name: release-gate
|
||||
needs: [ci, kernel-rig, packages]
|
||||
if: always()
|
||||
runs-on: blacksmith-2vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: Every part passed
|
||||
env:
|
||||
CI: ${{ needs.ci.result }}
|
||||
KERNEL_RIG: ${{ needs.kernel-rig.result }}
|
||||
PACKAGES: ${{ needs.packages.result }}
|
||||
run: |
|
||||
echo "ci=$CI kernel-rig=$KERNEL_RIG packages=$PACKAGES"
|
||||
test "$CI" = success
|
||||
test "$KERNEL_RIG" = success
|
||||
test "$PACKAGES" = success
|
||||
@@ -21,6 +21,8 @@
|
||||
# releases/latest/download/<name> links in the README keep working across releases.
|
||||
#
|
||||
# A dry run: dispatch with draft=false (the default). Only the release branch may create drafts.
|
||||
# The release gate (release-gate.yml) calls it as such a dry run, without secrets (unsigned) and
|
||||
# without the release job (packages_only).
|
||||
# A dry run from any branch also test-signs the Windows files when SIGNPATH_API_TOKEN exists:
|
||||
# gh workflow run release.yml --ref <branch> -f draft=false [-f signpath_signing_policy=test-signing]
|
||||
name: Release
|
||||
@@ -41,6 +43,25 @@ on:
|
||||
description: "SignPath signing policy for the Windows files (empty: the repository variable, else test-signing)"
|
||||
type: string
|
||||
default: ""
|
||||
# The release gate (release-gate.yml): a dry run (draft false) of every package and its checks.
|
||||
# The event stays the caller's (workflow_dispatch), so a call runs as a dispatched dry run does.
|
||||
# The gate passes no secrets, so nothing is signed (the signing steps skip without their
|
||||
# secrets, as on a fork), and packages_only skips the release job (checksums, update signing,
|
||||
# publishing).
|
||||
workflow_call:
|
||||
inputs:
|
||||
packages_only:
|
||||
description: "Build and check every package only: no release job"
|
||||
type: boolean
|
||||
default: false
|
||||
draft:
|
||||
description: "Create/update a draft (release branch only; off: build artifacts only)"
|
||||
type: boolean
|
||||
default: false
|
||||
signpath_signing_policy:
|
||||
description: "SignPath signing policy for the Windows files (empty: the repository variable, else test-signing)"
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -213,6 +234,8 @@ jobs:
|
||||
if: needs.version.outputs.build == 'true'
|
||||
# makepkg only repackages the tarball's prebuilt binaries, so both architectures build here.
|
||||
runs-on: ubuntu-24.04
|
||||
# A pre-release or nightly without an Arch package is still worth having; a stable release isn't.
|
||||
continue-on-error: ${{ contains(needs.version.outputs.version, '-') }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -229,10 +252,29 @@ jobs:
|
||||
# makepkg in an archlinux:base-devel container. The package is named after the tarball's
|
||||
# .desktop file (io.tangencylabs.solidcraft) and written as
|
||||
# dist/release/solidcraft-<version>-linux-<arch>.pkg.tar.zst.
|
||||
# An Arch version can't hold a hyphen, and the action reads the version from the tarball's name
|
||||
# (a hyphenated one comes out as 0.0.0): 0.40.0-rc.1 goes in as 0.40.0rc.1, which pacman sorts
|
||||
# before 0.40.0, and the package keeps the release's name.
|
||||
- id: pkgver
|
||||
name: Arch version
|
||||
run: |
|
||||
pkgver="${VERSION//-/}"
|
||||
echo "pkgver=$pkgver" >>"$GITHUB_OUTPUT"
|
||||
if [ "$pkgver" != "$VERSION" ]; then
|
||||
cp "dist/linux/solidcraft-$VERSION-linux-$ARCH.tar.gz" "dist/linux/solidcraft-$pkgver-linux-$ARCH.tar.gz"
|
||||
fi
|
||||
env:
|
||||
ARCH: ${{ matrix.arch }}
|
||||
- uses: storytold/craft-common-actions/actions/arch-package@fa5ef18780cf5e7d5d3b8de068de56cffc83cac5 # main
|
||||
with:
|
||||
tarball: dist/linux/solidcraft-${{ env.VERSION }}-linux-${{ matrix.arch }}.tar.gz
|
||||
tarball: dist/linux/solidcraft-${{ steps.pkgver.outputs.pkgver }}-linux-${{ matrix.arch }}.tar.gz
|
||||
arch: ${{ matrix.arch }}
|
||||
- name: The package under the release's version
|
||||
if: steps.pkgver.outputs.pkgver != env.VERSION
|
||||
env:
|
||||
ARCH: ${{ matrix.arch }}
|
||||
PKGVER: ${{ steps.pkgver.outputs.pkgver }}
|
||||
run: sudo mv "dist/release/solidcraft-$PKGVER-linux-$ARCH.pkg.tar.zst" "dist/release/solidcraft-$VERSION-linux-$ARCH.pkg.tar.zst"
|
||||
- name: Check the package
|
||||
env:
|
||||
PKG: dist/release/solidcraft-${{ env.VERSION }}-linux-${{ matrix.arch }}.pkg.tar.zst
|
||||
@@ -612,7 +654,8 @@ jobs:
|
||||
release:
|
||||
name: GitHub Release or dry-run artifacts
|
||||
needs: [version, linux, flatpak, arch, snap, freebsd, macos, windows, web]
|
||||
if: needs.version.outputs.build == 'true'
|
||||
# Not for the release gate (packages_only): it builds and checks the packages, nothing more.
|
||||
if: needs.version.outputs.build == 'true' && !inputs.packages_only
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
@@ -10,6 +10,7 @@ Work: ROADMAP.md, "Where help is wanted most" in CONTRIBUTING.md, issues labelle
|
||||
- No GPL/LGPL/AGPL code, not even reading it (OpenCASCADE, FreeCAD, SolveSpace, CadQuery, LibreCAD, QCAD): `cargo xtask licences --check`.
|
||||
- Every asset is original or openly licensed with a row in ATTRIBUTION.md; read `assets/AGENTS.md` before icon work: `cargo xtask assets`, `cargo xtask icons`.
|
||||
- Never crash: no unwrap/expect/panic/unsafe in non-test code (unsafe allowlist in crash-safety), errors are `Result`, input is hostile: clippy `-D warnings`, `hostile_params_never_panic`.
|
||||
- Model decisions (what an operation does, which bodies it touches, which face or edge a point or name means, depths, nearness) use exact geometry; a mesh is for display, export, previews and mesh bodies (`xtask/mesh-decisions.txt` only shrinks): `cargo xtask mesh-decisions`.
|
||||
- Everything is a command and programmatic calls never open dialogs: `skills/solidcraft-commands/SKILL.md`.
|
||||
- Layering (geom, sketch/kernel/render, doc, io, engine, ui-egui, apps); only `crates/kernel` uses truck: `cargo xtask layers`.
|
||||
- One outer UI container, one camera path (`navigation::handle`), colours from theme tokens: `cargo xtask ui-containers`.
|
||||
@@ -22,6 +23,7 @@ Work: ROADMAP.md, "Where help is wanted most" in CONTRIBUTING.md, issues labelle
|
||||
- A changed default keeps the old behaviour behind a preference and fills the PR's `Changes a default:` line, and no second PR for an issue a lower-numbered one closes: `pr-checks.yml`.
|
||||
- A regression test goes in the area's existing test file, never a per-bug file (#3226).
|
||||
- Look at open PRs first, claim with a draft PR (lowest number wins), one topic per PR, never force-push `main`, no pushes just to catch up with `main`: `skills/solidcraft-contributing/SKILL.md`.
|
||||
- A PR's title and first sentence make sense to someone who never saw your chat or the issue: `skills/solidcraft-contributing/SKILL.md`.
|
||||
- Show observable changes with real screenshots or a recording, captured yourself.
|
||||
- Run `cargo xtask ci` before every commit, without switching branches while it runs. `cargo xtask skills` keeps this file and `skills/` honest.
|
||||
|
||||
|
||||
+28
-5
@@ -103,6 +103,22 @@ Editable drawing code, native projects and shipped exports are collected under `
|
||||
| tests/step-conformance/tiny_pin.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
|
||||
| tests/step-conformance/torus_ring_nurbs.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
|
||||
| tests/step-conformance/washer.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
|
||||
| tests/kernel-cases/import/1721-closed-bspline-tube-face/input.step | step-conformance contributors | copy of tests/step-conformance/cylinder_nurbs.step (generated from formulas by https://github.com/dan335/step-conformance, corpus/generated, 96f30bb); kernel regression case for tangency-labs/solidcraft#1721 | CC0-1.0 |
|
||||
| tests/kernel-cases/import/2043-outer-bound-opposing-surface-sense/input.step | SolidCraft contributors | synthetic STEP file written by hand from ISO 10303-21 for the kernel regression case of tangency-labs/solidcraft#2043 | MIT OR Apache-2.0 |
|
||||
| tests/kernel-cases/import/258-cone-point-vertex/input.step | step-conformance contributors | copy of tests/step-conformance/cone_point_vertex.step (generated from formulas by https://github.com/dan335/step-conformance, corpus/generated, 96f30bb); kernel regression case for tangency-labs/solidcraft#258 | CC0-1.0 |
|
||||
| tests/kernel-cases/import/258-cone-to-point/input.step | step-conformance contributors | copy of tests/step-conformance/cone_point.step (generated from formulas by https://github.com/dan335/step-conformance, corpus/generated, 96f30bb); kernel regression case for tangency-labs/solidcraft#258 | CC0-1.0 |
|
||||
| tests/kernel-cases/import/258-tiny-pin/input.step | step-conformance contributors | copy of tests/step-conformance/tiny_pin.step (generated from formulas by https://github.com/dan335/step-conformance, corpus/generated, 96f30bb); kernel regression case for tangency-labs/solidcraft#258 | CC0-1.0 |
|
||||
| tests/kernel-cases/import/3098-bspline-edges-over-tiny-knot-span/input.step | SolidCraft contributors | synthetic STEP file written by hand from ISO 10303-21 for the kernel regression case of tangency-labs/solidcraft#3098 | MIT OR Apache-2.0 |
|
||||
| tests/kernel-cases/import/3208-extruded-ellipse-face-over-parameter-seam/input.step | SolidCraft contributors | synthetic STEP file written by hand from ISO 10303-21 for the kernel regression case of tangency-labs/solidcraft#3208 | MIT OR Apache-2.0 |
|
||||
| tests/kernel-cases/import/3352-vertex-off-its-edge-curve/input.step | SolidCraft contributors | synthetic STEP file written by hand from ISO 10303-21 for the kernel regression case of tangency-labs/solidcraft#3352 | MIT OR Apache-2.0 |
|
||||
| tests/kernel-cases/import/3730-void-oriented-shell-false/input.step | SolidCraft contributors | synthetic STEP file written by hand from ISO 10303-21 for the kernel regression case of tangency-labs/solidcraft#3730 | MIT OR Apache-2.0 |
|
||||
| tests/kernel-cases/import/3880-cylinder-seam-edge/input.step | step-conformance contributors | copy of tests/step-conformance/cylinder_seam.step (generated from formulas by https://github.com/dan335/step-conformance, corpus/generated, 96f30bb); kernel regression case for tangency-labs/solidcraft#3880 | CC0-1.0 |
|
||||
| tests/kernel-cases/import/3880-nurbs-hemisphere-dome/input.step | step-conformance contributors | copy of tests/step-conformance/dome_nurbs.step (generated from formulas by https://github.com/dan335/step-conformance, corpus/generated, 96f30bb); kernel regression case for tangency-labs/solidcraft#3880 | CC0-1.0 |
|
||||
| tests/kernel-cases/import/3963-closed-spline-edge-vertex-past-start/input.step | SolidCraft contributors | synthetic STEP file written by hand from ISO 10303-21 for the kernel regression case of tangency-labs/solidcraft#3963 | MIT OR Apache-2.0 |
|
||||
| tests/kernel-cases/import/4005-cylinder-written-as-near-zero-cone/input.step | SolidCraft contributors | synthetic STEP file written by hand from ISO 10303-21 for the kernel regression case of tangency-labs/solidcraft#4005 | MIT OR Apache-2.0 |
|
||||
| tests/kernel-cases/import/4006-plane-written-as-cone-of-nearly-right-angle/input.step | SolidCraft contributors | synthetic STEP file written by hand from ISO 10303-21 for the kernel regression case of tangency-labs/solidcraft#4006 | MIT OR Apache-2.0 |
|
||||
| tests/ssi-corpus/cases/*.json.gz, tests/ssi-corpus/LICENSE-MIT, tests/ssi-corpus/LICENSE-APACHE | ssi-corpus contributors (jyviko) | 119 surface/surface intersection cases with exact ground truth from https://github.com/jyviko/ssi-corpus (cases/data, b04c8a6), gzip-compressed, unchanged; SSI scorer fixtures (tests/ssi-corpus/README.md) | MIT OR Apache-2.0 |
|
||||
| tests/ssi-corpus/cases/y23_*.json.gz | Yang, Jia and Yan (paper authors: published surfaces, figures and topology); ssi-corpus contributors (reconstructions and truth) | 25 cases built from Yang, Jia & Yan 2023, "Topology Guaranteed B-Spline Surface/Surface Intersection", ACM Transactions on Graphics 42(6), Article 211; each cites the paper in its `source` field | CC-BY 4.0 (paper); MIT OR Apache-2.0 (case files) |
|
||||
| tests/step-regressions/rod_doubled_edge.step | SolidCraft contributors | written by SolidCraft from the commands of #4598 (a bored rod moved 100 mm and exported); import regression fixture | MIT OR Apache-2.0 |
|
||||
| tests/oracle/17-loft/reference.stl | SolidCraft contributors | triangulation of our own recipe (tests/oracle/17-loft/recipe.json) built with FreeCAD as an external tool by tools/freecad-oracle | MIT OR Apache-2.0 |
|
||||
| tests/oracle/23-bracket-multifeature/reference.stl | SolidCraft contributors | triangulation of our own recipe (tests/oracle/23-bracket-multifeature/recipe.json) built with FreeCAD as an external tool by tools/freecad-oracle | MIT OR Apache-2.0 |
|
||||
@@ -754,6 +770,8 @@ Original SolidCraft drawings, Open for owner review; no runtime assignments.
|
||||
| docs/design/preferences/figures/view-shadow.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/view-shadow.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| `assets/locales/pending/sketch-offset-instances.json` | SolidCraft contributors | Original offset instance-count label and translations | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/flat/settings_profile.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original analytic head and shoulder geometry for settings profiles | MIT OR Apache-2.0 |
|
||||
| tests/kernel-cases/import/4561-convert-box-mesh-with-a-zero-area-triangle/box-degenerate.stl | SolidCraft contributors | synthetic ASCII STL written by hand: a box with one zero-area triangle, for the kernel regression case of tangency-labs/solidcraft#4561 | MIT OR Apache-2.0 |
|
||||
|
||||
## Code adapted from sibling projects
|
||||
|
||||
@@ -793,6 +811,15 @@ in THIRD-PARTY-LICENSES.txt; no images or artwork from that project are copied.
|
||||
The glTF test files are built by the tests themselves (crates/io/src/gltf_in_tests.rs,
|
||||
crates/engine/src/tests/gltf_import.rs); no third-party models are committed.
|
||||
|
||||
## Geometry kernel crates
|
||||
|
||||
| Crate | Used for | Licence |
|
||||
|---|---|---|
|
||||
| solidcore (https://github.com/tangency-labs/solidcore, pinned by rev in Cargo.toml) | the Rust port of NVIDIA's SMLib: the B-rep kernel behind crates/smlib | Apache-2.0 (its NOTICE credits NVIDIA's USD BRep) |
|
||||
|
||||
Linked as a crate; no code from it is copied into SolidCraft. Its licence text and NOTICE go
|
||||
into THIRD-PARTY-LICENSES.txt when `main` regenerates it.
|
||||
|
||||
## FreeCAD and Rhino import library
|
||||
|
||||
FreeCAD `.FCStd` and Rhino `.3dm` files are read with [cadmpeg](https://github.com/cadmpeg/cadmpeg)
|
||||
@@ -808,17 +835,13 @@ copied into SolidCraft.
|
||||
| SolidCraft file | Original contribution | Licence |
|
||||
|---|---|---|
|
||||
| crates/ui-egui/src/viewport.rs (`nearest_quarter_turn`, `axis_view_orbit`) | N3MI-DG's [PR #101](https://github.com/bherbruck/solidcraft/pull/101): [minimal TOP/BOTTOM turns](https://github.com/bherbruck/solidcraft/commit/c28e780f52490784ef5886f094b9d3f6d10c3e3f) and [drag-to-orbit](https://github.com/bherbruck/solidcraft/commit/f30b30be6ecc4e2e6544a851243137c01a354934), adapted to the axis gizmo | MIT OR Apache-2.0 |
|
||||
| crates/smlib/src/core/offset.rs (`draft`: the tilted planes and `SmBrep::LocalOperation`, the tail of the capi's `draft_faces`) | ablasik's `draft` in `crates/smlib/src/ffi/rs_ops2.rs` on [ablasik/solidcraft `app/solidcore-backend-on-develop`](https://github.com/ablasik/solidcraft/blob/9c7e8e31565e1823f0591b58e2967351c32af13f/crates/smlib/src/ffi/rs_ops2.rs), adapted to `core::offset`'s errors and checks | MIT OR Apache-2.0 |
|
||||
|
||||
## Vendored third-party code
|
||||
|
||||
| Directory | Source | Licence | Changes |
|
||||
|---|---|---|---|
|
||||
| vendor/accesskit_macos | accesskit_macos 0.26.3, https://github.com/AccessKit/accesskit (The AccessKit contributors) | MIT OR Apache-2.0 | the subclassing adapter and its tests from 0.27.2: dropping the adapter keeps AppKit's key-value observers (vendor/README.md) |
|
||||
| vendor/truck-meshalgo | truck-meshalgo 0.4.0, https://github.com/ricosjp/truck (RICOS Co. Ltd.) | Apache-2.0 | meshing: bounded parameter searches, collapsed-boundary projection, subdivision and provisional fixed-grid previews (vendor/README.md) |
|
||||
| vendor/truck-stepio | truck-stepio 0.3.0, https://github.com/ricosjp/truck (RICOS Co. Ltd.) | Apache-2.0 | STEP writer: intersection-curve surfaces written at unique ids (vendor/README.md) |
|
||||
| vendor/truck-shapeops | truck-shapeops 0.4.0, https://github.com/ricosjp/truck (RICOS Co. Ltd.) | Apache-2.0 | robust meshing and nearest-parameter fallback in booleans; degenerate intersection-curve rejection; face and edge history of booleans; trace macro; corner choice at vertices several intersection curves end on; reuse of an operand's meshes (vendor/README.md) |
|
||||
| vendor/truck-geometry | truck-geometry 0.5.0, https://github.com/ricosjp/truck (RICOS Co. Ltd.) | Apache-2.0 | intersection curves divided from their neighbours' surface parameters; surface second derivatives (vendor/README.md) |
|
||||
| vendor/matext4cgmath | matext4cgmath 0.1.0, https://github.com/ricosjp/matext4cgmath (RICOS Co. Ltd.) | Apache-2.0 | root refinement stops after 100 Newton steps (vendor/README.md) |
|
||||
|
||||
## Vendored renderer compatibility fix
|
||||
|
||||
|
||||
+24
-6
@@ -161,9 +161,11 @@ the most complete description of the house style.
|
||||
| UI scenarios | `crates/ui-egui/tests/scenarios/*.json` | part of `cargo test` |
|
||||
| Reference tests | `cargo xtask oracle` | `tests/oracle/` (see below) |
|
||||
|
||||
A geometry change that changes the robustness goldens re-records them in the same pull request:
|
||||
`ROBUSTNESS_GOLDENS_BLESS="why it changed" cargo test --profile ci -p solidcraft-engine --lib robustness_goldens::`
|
||||
(see [docs/robustness.md](docs/robustness.md)); CI runs every seed on pull requests that touch geometry.
|
||||
The reference for what the kernel builds is the kernel-cases ladder (`tests/kernel-cases/ladder/`,
|
||||
`cargo xtask kernel-cases --ladder`, [docs/kernel-ladder.md](docs/kernel-ladder.md)).
|
||||
The naming corpus checks persistent names: every name a corpus design stores still finds a face or
|
||||
an edge, and two builds name its faces and edges alike
|
||||
(`cargo test --profile ci -p solidcraft-cli --test naming_corpus`).
|
||||
[docs/mutation-testing.md](docs/mutation-testing.md) shows how to find kernel behaviour these
|
||||
black-box suites miss.
|
||||
|
||||
@@ -222,8 +224,7 @@ Pull requests skip a few heavy tests (one to twelve minutes each), which run in
|
||||
Check that validation after merging. They are listed in
|
||||
`.config/nextest.toml` and the [testing page](docs/book/src/dev/testing.md):
|
||||
|
||||
- `solidcraft-engine`: `tests::hostile_params_never_panic`, everything in `tests::robustness` and
|
||||
the seeds of `tests::robustness_goldens` (`seed_00` … `seed_95`; pull requests that change the kernel, truck or the goldens run all but the slowest, `SLOW_SEEDS` in `ci.yml`),
|
||||
- `solidcraft-engine`: `tests::hostile_params_never_panic`, everything in `tests::robustness`,
|
||||
`pattern_suppress_tests::enclosure_arc_extinction_keeps_a_real_sharp_inner_lip_after_resize_and_reopen`,
|
||||
`pattern_suppress_tests::enclosure_datums_follow_wall_height_and_board_depth_with_material_proofs`,
|
||||
`cmd::plastic::tests::enclosure_snap_fit_follows_resized_front_wall`, the `atomic_commands` contract tests
|
||||
@@ -243,7 +244,8 @@ the surface itself). Expected values are either worked out in closed form
|
||||
(`tools/oracle-analytic/`) or computed by building the same recipe in FreeCAD, run as a separate
|
||||
program (`tools/freecad-oracle/`); each file says which. The generated
|
||||
[reference test report](docs/oracle.md) has the current case count and results. The reference-test
|
||||
CI job runs `cargo xtask oracle` during full validation; pull-request runs skip it. A case that
|
||||
CI job runs `cargo xtask oracle` during full validation, and on pull requests that reach
|
||||
`solidcraft-cli`. A case that
|
||||
fails because of a known bug names its issue in `expected.json` (`"known_failure": "#123"`);
|
||||
never change an expected value to match SolidCraft's output.
|
||||
|
||||
@@ -339,6 +341,15 @@ and a fresh CI run:
|
||||
PRs in groups by area, so PRs that touch the same code meet a conflict at most once.
|
||||
4. Reference the issue (`Fixes #12`). In the PR, say what you tested and how. For geometry work, include the measured numbers (and
|
||||
the expected ones).
|
||||
5. Write the PR title and first sentence for someone who hasn't seen your chat, the issue or the
|
||||
code. Maintainers pick what to review from the title, and it becomes the commit on `main`. Say
|
||||
which part of the app changes and what a user now sees or can do, and name things rather than
|
||||
leaning on "there", "it" or "this".
|
||||
- Unclear: `Send to Slicer: pick one print face per body there, and show print faces only while it is open`
|
||||
- Clear: `3D printing: choose the face each body rests on in the Send to Slicer window, not the face menu`
|
||||
|
||||
Use plain, literal English in PR text: one idea per sentence, active voice, and one word for one
|
||||
thing. This helps everyone, including readers whose first language isn't English.
|
||||
|
||||
Commenting `/claim` assigns you, adds the `in progress` label, and replies with its automatic cleanup
|
||||
deadline. It frees a claim with no linked PR after 72 hours, or a claim with a PR after 14 days
|
||||
@@ -347,6 +358,13 @@ without pushes. These automatic cleanup deadlines are separate from the daily pr
|
||||
`/unclaim` releases the bot claim early (only you or a maintainer can). Issues a maintainer has
|
||||
taken are never freed automatically.
|
||||
|
||||
**A PR has to explain itself.** Start the description with what it is, in plain words someone new
|
||||
to the project would understand: what it does and who it's for. Don't assume anyone knows the name
|
||||
of your tool, project or feature. If there's anything to look at, show it in the description: a
|
||||
picture or video has to be visible there without opening the changed files. "The GIF is the change"
|
||||
doesn't count. A PR that doesn't do this waits until it does. For a PR that adds to
|
||||
`docs/showcase.md` or `docs/screenshots/`, the Picture check fails until the description shows one.
|
||||
|
||||
Help reviewers see what the words mean. For observable CAD, UI or model behavior, please
|
||||
include actual descriptive screenshots or a short recording in the issue or PR. This evidence
|
||||
is required for changes or bugs that can be demonstrated in the app, including kernel,
|
||||
|
||||
Generated
+63
-327
@@ -186,15 +186,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "approx"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arboard"
|
||||
version = "3.6.1"
|
||||
@@ -215,12 +206,6 @@ dependencies = [
|
||||
"x11rb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "array-macro"
|
||||
version = "2.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "220a2c618ab466efe41d0eace94dfeff1c35e3aa47891bdb95e1c0fefffd3c99"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.9"
|
||||
@@ -533,15 +518,6 @@ dependencies = [
|
||||
"piper",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "branches"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f11502672c5570f77f6bdf573332483f8475bab6a7fda00f1fae8ddb5a6245c0"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.20.3"
|
||||
@@ -789,17 +765,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cgmath"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317"
|
||||
dependencies = [
|
||||
"approx",
|
||||
"num-traits",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chacha20"
|
||||
version = "0.10.2"
|
||||
@@ -822,19 +787,6 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
@@ -905,12 +857,6 @@ version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
@@ -1088,19 +1034,6 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
@@ -1247,7 +1180,7 @@ dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"emath",
|
||||
"epaint",
|
||||
"itertools 0.15.0",
|
||||
"itertools",
|
||||
"log",
|
||||
"nohash-hasher",
|
||||
"profiling",
|
||||
@@ -1665,10 +1598,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1983,30 +1914,6 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "2.3.0"
|
||||
@@ -2166,15 +2073,6 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.15.0"
|
||||
@@ -2269,17 +2167,6 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "katexit"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccfb0b7ce7938f84a5ecbdca5d0a991e46bc9d6d078934ad5e92c5270fe547db"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keyboard-types"
|
||||
version = "0.8.3"
|
||||
@@ -2430,15 +2317,6 @@ version = "0.4.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
|
||||
|
||||
[[package]]
|
||||
name = "matext4cgmath"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cgmath",
|
||||
"katexit",
|
||||
"num-complex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.3"
|
||||
@@ -3314,30 +3192,6 @@ dependencies = [
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.107"
|
||||
@@ -3510,15 +3364,6 @@ dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rclite"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e09665c494a9de2bd230e1aedc0c2fb20481a51f28f7db84d0c9bcfe2fe537b2"
|
||||
dependencies = [
|
||||
"branches",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "read-fonts"
|
||||
version = "0.41.0"
|
||||
@@ -4206,9 +4051,17 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solidcore"
|
||||
version = "1.0.1"
|
||||
source = "git+https://github.com/tangency-labs/solidcore?rev=8ddce901671f1cebd9214a31d21895d725e886c8#8ddce901671f1cebd9214a31d21895d725e886c8"
|
||||
dependencies = [
|
||||
"thiserror 2.0.21",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft"
|
||||
version = "0.30.0"
|
||||
version = "0.40.0-rc.1"
|
||||
dependencies = [
|
||||
"block2 0.6.2",
|
||||
"eframe",
|
||||
@@ -4245,23 +4098,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-3dx-client"
|
||||
version = "0.30.0"
|
||||
version = "0.40.0-rc.1"
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-cli"
|
||||
version = "0.30.0"
|
||||
version = "0.40.0-rc.1"
|
||||
dependencies = [
|
||||
"image",
|
||||
"log",
|
||||
"serde_json",
|
||||
"solidcraft-engine",
|
||||
"solidcraft-gpu",
|
||||
"solidcraft-kernel",
|
||||
"solidcraft-mcp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-derive"
|
||||
version = "0.30.0"
|
||||
version = "0.40.0-rc.1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4270,7 +4124,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-doc"
|
||||
version = "0.30.0"
|
||||
version = "0.40.0-rc.1"
|
||||
dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
@@ -4279,6 +4133,7 @@ dependencies = [
|
||||
"solidcraft-derive",
|
||||
"solidcraft-geom",
|
||||
"solidcraft-kernel",
|
||||
"solidcraft-profile",
|
||||
"solidcraft-render",
|
||||
"solidcraft-sketch",
|
||||
"thiserror 2.0.21",
|
||||
@@ -4287,7 +4142,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-engine"
|
||||
version = "0.30.0"
|
||||
version = "0.40.0-rc.1"
|
||||
dependencies = [
|
||||
"image",
|
||||
"log",
|
||||
@@ -4299,6 +4154,7 @@ dependencies = [
|
||||
"solidcraft-geom",
|
||||
"solidcraft-io",
|
||||
"solidcraft-kernel",
|
||||
"solidcraft-profile",
|
||||
"solidcraft-render",
|
||||
"solidcraft-sketch",
|
||||
"thiserror 2.0.21",
|
||||
@@ -4308,7 +4164,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-geom"
|
||||
version = "0.30.0"
|
||||
version = "0.40.0-rc.1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4317,7 +4173,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-gpu"
|
||||
version = "0.30.0"
|
||||
version = "0.40.0-rc.1"
|
||||
dependencies = [
|
||||
"image",
|
||||
"log",
|
||||
@@ -4337,7 +4193,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-io"
|
||||
version = "0.30.0"
|
||||
version = "0.40.0-rc.1"
|
||||
dependencies = [
|
||||
"cadmpeg-core",
|
||||
"cadmpeg-ir",
|
||||
@@ -4355,6 +4211,7 @@ dependencies = [
|
||||
"solidcraft-doc",
|
||||
"solidcraft-geom",
|
||||
"solidcraft-kernel",
|
||||
"solidcraft-profile",
|
||||
"solidcraft-testkit",
|
||||
"thiserror 2.0.21",
|
||||
"zip",
|
||||
@@ -4362,7 +4219,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-kernel"
|
||||
version = "0.30.0"
|
||||
version = "0.40.0-rc.1"
|
||||
dependencies = [
|
||||
"log",
|
||||
"proptest",
|
||||
@@ -4370,19 +4227,17 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"solidcraft-geom",
|
||||
"solidcraft-profile",
|
||||
"solidcraft-smlib",
|
||||
"solidcraft-step",
|
||||
"spade",
|
||||
"thiserror 2.0.21",
|
||||
"truck-meshalgo",
|
||||
"truck-modeling",
|
||||
"truck-shapeops",
|
||||
"truck-stepio",
|
||||
"truck-topology",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-mcp"
|
||||
version = "0.30.0"
|
||||
version = "0.40.0-rc.1"
|
||||
dependencies = [
|
||||
"image",
|
||||
"serde_json",
|
||||
@@ -4390,9 +4245,17 @@ dependencies = [
|
||||
"solidcraft-gpu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-profile"
|
||||
version = "0.40.0-rc.1"
|
||||
dependencies = [
|
||||
"serde_json",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-render"
|
||||
version = "0.30.0"
|
||||
version = "0.40.0-rc.1"
|
||||
dependencies = [
|
||||
"image",
|
||||
"moxcms",
|
||||
@@ -4407,7 +4270,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-sketch"
|
||||
version = "0.30.0"
|
||||
version = "0.40.0-rc.1"
|
||||
dependencies = [
|
||||
"harfrust",
|
||||
"serde",
|
||||
@@ -4418,20 +4281,40 @@ dependencies = [
|
||||
"unicode-bidi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-smlib"
|
||||
version = "0.40.0-rc.1"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"log",
|
||||
"serde_json",
|
||||
"solidcore",
|
||||
"solidcraft-geom",
|
||||
"solidcraft-profile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-space-mouse"
|
||||
version = "0.30.0"
|
||||
version = "0.40.0-rc.1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-step"
|
||||
version = "0.40.0-rc.1"
|
||||
dependencies = [
|
||||
"solidcraft-geom",
|
||||
"solidcraft-profile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-testkit"
|
||||
version = "0.30.0"
|
||||
version = "0.40.0-rc.1"
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-ui-egui"
|
||||
version = "0.30.0"
|
||||
version = "0.40.0-rc.1"
|
||||
dependencies = [
|
||||
"arboard",
|
||||
"egui",
|
||||
@@ -4462,7 +4345,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-web"
|
||||
version = "0.30.0"
|
||||
version = "0.40.0-rc.1"
|
||||
dependencies = [
|
||||
"eframe",
|
||||
"egui",
|
||||
@@ -4523,16 +4406,6 @@ version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.119"
|
||||
@@ -4782,144 +4655,6 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "truck-base"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c279de9e92e5dc20a188deb0bb9a4bd421a6a185e57e03e19025e84a36c5a05"
|
||||
dependencies = [
|
||||
"cgmath",
|
||||
"matext4cgmath",
|
||||
"rustc-hash 2.1.3",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "truck-derivers"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "421ff1c5a303aed64e295d9b30e8424ee7a2fa251617a59ae4acfd078496009f"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "truck-geometry"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"thiserror 1.0.69",
|
||||
"truck-base",
|
||||
"truck-geotrait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "truck-geotrait"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97a31fe98d5a0589ad20576260e214af23dea21a8d125da81ad3bbcb666f411f"
|
||||
dependencies = [
|
||||
"getrandom 0.2.17",
|
||||
"rand 0.8.8",
|
||||
"thiserror 1.0.69",
|
||||
"truck-base",
|
||||
"truck-derivers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "truck-meshalgo"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"array-macro",
|
||||
"derive_more",
|
||||
"itertools 0.13.0",
|
||||
"rayon",
|
||||
"rustc-hash 2.1.3",
|
||||
"spade",
|
||||
"truck-base",
|
||||
"truck-geometry",
|
||||
"truck-modeling",
|
||||
"truck-polymesh",
|
||||
"truck-topology",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "truck-modeling"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8082c2dfac8c2732a014d81807fb5d24dbac0d72ee4de2a705b7fbc05fa05b18"
|
||||
dependencies = [
|
||||
"derive_more",
|
||||
"rustc-hash 2.1.3",
|
||||
"serde",
|
||||
"thiserror 1.0.69",
|
||||
"truck-base",
|
||||
"truck-geometry",
|
||||
"truck-geotrait",
|
||||
"truck-polymesh",
|
||||
"truck-topology",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "truck-polymesh"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8f22def310af4a89c37beb4839bbf702b302d2c112ea29553759f0e16622fbf"
|
||||
dependencies = [
|
||||
"array-macro",
|
||||
"bytemuck",
|
||||
"itertools 0.13.0",
|
||||
"rustc-hash 2.1.3",
|
||||
"serde",
|
||||
"thiserror 1.0.69",
|
||||
"truck-base",
|
||||
"truck-geotrait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "truck-shapeops"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"derive_more",
|
||||
"itertools 0.13.0",
|
||||
"rustc-hash 2.1.3",
|
||||
"truck-base",
|
||||
"truck-geometry",
|
||||
"truck-geotrait",
|
||||
"truck-meshalgo",
|
||||
"truck-topology",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "truck-stepio"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"truck-geometry",
|
||||
"truck-modeling",
|
||||
"truck-polymesh",
|
||||
"truck-topology",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "truck-topology"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf62a1610405c5e39ab5eb6948f326fc1641689734a6b8207a80792133fe0e78"
|
||||
dependencies = [
|
||||
"parking_lot",
|
||||
"rayon",
|
||||
"rclite",
|
||||
"rustc-hash 2.1.3",
|
||||
"serde",
|
||||
"thiserror 1.0.69",
|
||||
"truck-base",
|
||||
"truck-geotrait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ttf-parser"
|
||||
version = "0.25.1"
|
||||
@@ -6017,9 +5752,10 @@ checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7"
|
||||
|
||||
[[package]]
|
||||
name = "xtask"
|
||||
version = "0.30.0"
|
||||
version = "0.40.0-rc.1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quick-xml",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.9",
|
||||
|
||||
+13
-19
@@ -5,7 +5,7 @@ members = ["crates/*", "apps/*", "xtask"]
|
||||
exclude = ["vendor"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.30.0"
|
||||
version = "0.40.0-rc.1"
|
||||
edition = "2024"
|
||||
license = "MIT OR Apache-2.0"
|
||||
rust-version = "1.95"
|
||||
@@ -16,8 +16,11 @@ repository = "https://github.com/bherbruck/solidcraft"
|
||||
robust = "1.2"
|
||||
# internal
|
||||
solidcraft-geom = { path = "crates/geom" }
|
||||
solidcraft-profile = { path = "crates/profile" }
|
||||
solidcraft-sketch = { path = "crates/sketch" }
|
||||
solidcraft-kernel = { path = "crates/kernel" }
|
||||
solidcraft-smlib = { path = "crates/smlib" }
|
||||
solidcraft-step = { path = "crates/step" }
|
||||
solidcraft-render = { path = "crates/render" }
|
||||
solidcraft-gpu = { path = "crates/gpu" }
|
||||
solidcraft-doc = { path = "crates/doc" }
|
||||
@@ -28,6 +31,11 @@ solidcraft-space-mouse = { path = "crates/space-mouse" }
|
||||
solidcraft-mcp = { path = "crates/mcp" }
|
||||
solidcraft-derive = { path = "crates/derive" }
|
||||
solidcraft-3dx-client = { path = "crates/threedx-client" }
|
||||
# The Rust port of NVIDIA's SMLib (Apache-2.0), the B-rep kernel behind crates/smlib. Pinned;
|
||||
# move the rev on its own commit. For a
|
||||
# local checkout, add `[patch."https://github.com/tangency-labs/solidcore"] solidcore = { path =
|
||||
# "../solidcore/crates/solidcore" }` to a `.cargo/config.toml` outside the repository.
|
||||
solidcore = { git = "https://github.com/tangency-labs/solidcore", rev = "8ddce901671f1cebd9214a31d21895d725e886c8" }
|
||||
# test tooling: a dev-dependency only (xtask/src/layers.rs)
|
||||
solidcraft-testkit = { path = "crates/testkit" }
|
||||
|
||||
@@ -47,14 +55,7 @@ sha2 = "0.10"
|
||||
moxcms = { version = "0.8.1", default-features = false, features = ["extended_range"] }
|
||||
semver = "1"
|
||||
|
||||
# geometry kernel (Apache-2.0; see plan/adr/0001-geometry-kernel.md). Only crates/kernel uses it.
|
||||
truck-modeling = "0.6"
|
||||
truck-shapeops = { version = "0.4", default-features = false }
|
||||
truck-meshalgo = { version = "0.4", default-features = false, features = ["tessellation", "analyzers"] }
|
||||
truck-stepio = { version = "0.3", default-features = false }
|
||||
truck-topology = "0.6"
|
||||
# constrained Delaunay + Ruppert refinement for trimmed faces (MIT OR Apache-2.0); already in the
|
||||
# tree through truck-meshalgo.
|
||||
# constrained Delaunay + Ruppert refinement for trimmed faces (MIT OR Apache-2.0).
|
||||
spade = { version = "2.15", default-features = false, features = ["std"] }
|
||||
rayon = "1.12"
|
||||
|
||||
@@ -127,9 +128,7 @@ opt-level = 2
|
||||
opt-level = 1
|
||||
|
||||
# Tests and lints (`cargo xtask ci`, GitHub CI): quick to build, quick enough to run. Light
|
||||
# optimisation, line tables only (file:line in backtraces), no LTO. Debug assertions are off because
|
||||
# truck's debug-only checks reject valid geometry (seam edges, closed edges); that was the only
|
||||
# reason the tests used to build with --release, which cost most of CI's time.
|
||||
# optimisation, line tables only (file:line in backtraces), no LTO, no debug assertions.
|
||||
[profile.ci]
|
||||
inherits = "dev"
|
||||
opt-level = 1
|
||||
@@ -152,14 +151,9 @@ codegen-units = 16
|
||||
# binary). See docs/crash-reports.md.
|
||||
debug = "line-tables-only"
|
||||
|
||||
# truck-shapeops with SolidCraft fixes (Apache-2.0, see vendor/truck-shapeops and ATTRIBUTION.md).
|
||||
[patch.crates-io]
|
||||
truck-geometry = { path = "vendor/truck-geometry" }
|
||||
truck-shapeops = { path = "vendor/truck-shapeops" }
|
||||
truck-stepio = { path = "vendor/truck-stepio" }
|
||||
truck-meshalgo = { path = "vendor/truck-meshalgo" }
|
||||
matext4cgmath = { path = "vendor/matext4cgmath" }
|
||||
|
||||
# Vendored crates with SolidCraft fixes (see vendor/README.md and ATTRIBUTION.md).
|
||||
[patch.crates-io]
|
||||
# Keep OpenGL pipelines, resolve textures and CAD callbacks at the same safe sample count.
|
||||
egui-wgpu = { path = "vendor/egui-wgpu" }
|
||||
|
||||
|
||||
+3
-1
@@ -662,7 +662,9 @@ completed drawings publish atomically. The browser still exports synchronously.
|
||||
pieces survive unfold/refold as one logical sheet group, including split flanged sheets.
|
||||
Cut-outs made in the flat pattern cross bends at any angle or curved: their edges fold to
|
||||
helices and curves on the bend, their walls square to the sheet. Free-form bases use bounded
|
||||
sampling.
|
||||
sampling. A fold crosses the sheet's own outer edges the same way, slanted or on any curve,
|
||||
with the outline exact in the flat pattern and its DXF, and flanges and other folds still go
|
||||
on the panels.
|
||||
Parallel prismatic partial-depth cuts retain pocket side and depth through transfer and
|
||||
unfold/refold, with separate pocket contours in preview and DXF. Planar Intersect preserves
|
||||
exact holes and retained islands, including the union of multiple selected profiles, and
|
||||
|
||||
+40
-12
@@ -3,7 +3,7 @@
|
||||
## Supported versions
|
||||
|
||||
SolidCraft is young and moves quickly, so only the latest release gets security fixes. Please
|
||||
update to the [latest release](https://github.com/bherbruck/solidcraft/releases/latest) before
|
||||
update to the [latest release](https://github.com/tangency-labs/solidcraft/releases/latest) before
|
||||
reporting, and check whether the problem is still on `main`.
|
||||
|
||||
| Version | Supported |
|
||||
@@ -15,18 +15,46 @@ reporting, and check whether the problem is still on `main`.
|
||||
## Reporting a vulnerability
|
||||
|
||||
Please **don't open a public issue** for a security problem. Report it privately through GitHub:
|
||||
open the repository's [Security tab](https://github.com/bherbruck/solidcraft/security) and choose
|
||||
open the repository's [Security tab](https://github.com/tangency-labs/solidcraft/security) and choose
|
||||
**Report a vulnerability**
|
||||
([direct link](https://github.com/bherbruck/solidcraft/security/advisories/new)).
|
||||
([direct link](https://github.com/tangency-labs/solidcraft/security/advisories/new)).
|
||||
|
||||
Please include what you found, how to reproduce it (a design file, command or MCP request), the
|
||||
version and platform, and the impact you expect. I'll acknowledge the report as soon as I can,
|
||||
keep you updated in the advisory, and credit you when the fix is published unless you'd rather
|
||||
stay anonymous.
|
||||
Please include what you found, the steps you ran to reproduce it (a design file, command or MCP
|
||||
request), the version and platform, and what an attacker gains. I'll acknowledge the report as soon
|
||||
as I can, keep you updated in the advisory, and credit you when the fix is published unless you'd
|
||||
rather stay anonymous.
|
||||
|
||||
Areas where reports are especially useful:
|
||||
## What counts as a security issue
|
||||
|
||||
- opening untrusted design, STEP, STL or 3MF files (crashes, excessive memory or CPU, path tricks)
|
||||
- the MCP server and the control API (file access outside the allowed folders, command injection)
|
||||
- the web app (anything reaching beyond the page's own storage)
|
||||
- release packages and the update paths (AppImage, Flatpak, MSI, deb, rpm, snap)
|
||||
Advisories are for problems where someone gets something they shouldn't, without already having
|
||||
access to your computer or your account. For example:
|
||||
|
||||
- opening a design, STEP, STL or 3MF file from someone else runs code, or reads or overwrites files
|
||||
it shouldn't
|
||||
- a website or another computer can reach or control SolidCraft
|
||||
- an agent gets past a prompt that promised to ask you first
|
||||
- the web app reaches beyond the page's own storage
|
||||
- a release package or an update can be tampered with
|
||||
- an API key or token gets sent somewhere it shouldn't go
|
||||
|
||||
## What to report as a normal bug
|
||||
|
||||
Please [open an issue](https://github.com/tangency-labs/solidcraft/issues/new) for these. They're
|
||||
still worth fixing, but they aren't security issues:
|
||||
|
||||
- anything that needs a program already running on your computer, or the control token. A program
|
||||
running as you can already do what you can.
|
||||
- MCP's allowed folders. They guard against accidents, they aren't a security boundary, and an agent
|
||||
with a shell of its own can already reach those files. Getting around them isn't a vulnerability.
|
||||
- crashes, hangs, or heavy memory or CPU use, unless they also lead to something in the list above
|
||||
- anything that only happens in an unusual setup you'd have to choose on purpose
|
||||
|
||||
## Before you send it
|
||||
|
||||
- **Run it.** Reproduce the problem on the latest release. If you only read the code, say so, and
|
||||
expect it to wait.
|
||||
- **Check it yourself.** If a scanner or an AI agent found it, read the code and confirm it first.
|
||||
These tools tend to overrate what they find.
|
||||
- **Say what's gained.** Name what the attacker needs and what they get. I set the final severity.
|
||||
|
||||
Reports that don't fit will be closed with a short note, or moved to the issue tracker.
|
||||
|
||||
+386
-1167
File diff suppressed because it is too large
Load Diff
@@ -20,9 +20,12 @@ name = "solvecraft-cli"
|
||||
path = "src/alias.rs"
|
||||
|
||||
[features]
|
||||
default = ["raytrace-cpu"]
|
||||
default = ["raytrace-cpu", "profile"]
|
||||
bundled-render-library = ["solidcraft-engine/bundled-render-library"]
|
||||
raytrace-cpu = ["solidcraft-engine/raytrace-cpu"]
|
||||
# The profiler, compiled in so any build can say where its time goes (`--profile`,
|
||||
# SOLIDCRAFT_PROFILE=1); it records nothing until asked.
|
||||
profile = ["solidcraft-engine/profile"]
|
||||
|
||||
[dependencies]
|
||||
solidcraft-engine = { workspace = true }
|
||||
@@ -34,3 +37,5 @@ serde_json = { workspace = true }
|
||||
[dev-dependencies]
|
||||
# Snapshot tests decode the pictures to compare them with a tolerance.
|
||||
image = { workspace = true }
|
||||
# Tests measure meshes at their own tolerances.
|
||||
solidcraft-kernel = { workspace = true }
|
||||
|
||||
@@ -184,7 +184,7 @@ pub fn run(args: &[String]) -> Result<(), String> {
|
||||
s.execute(id, &c["params"]).map_err(|e| format!("command {i} ({id}): {e}"))?;
|
||||
run.push(t.elapsed().as_secs_f64() * 1000.0);
|
||||
let t = std::time::Instant::now();
|
||||
s.world_state().warm_meshes(|| true);
|
||||
s.world_state().map_err(|e| e.to_string())?.warm_meshes(|| true);
|
||||
shown.push(t.elapsed().as_secs_f64() * 1000.0);
|
||||
if run.len() == 100 || i + 1 == commands.len() {
|
||||
let mean = |v: &[f64]| v.iter().sum::<f64>() / v.len().max(1) as f64;
|
||||
@@ -198,7 +198,7 @@ pub fn run(args: &[String]) -> Result<(), String> {
|
||||
shown.clear();
|
||||
}
|
||||
}
|
||||
let world = s.world_state();
|
||||
let world = s.world_state().map_err(|e| e.to_string())?;
|
||||
println!(
|
||||
"{}",
|
||||
json!({"step": "built", "ms": began.elapsed().as_secs_f64() * 1000.0, "commands": commands.len(), "features": s.doc.features.len(), "bodies": world.bodies.len()})
|
||||
@@ -236,7 +236,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
assert!(s.model.results.iter().all(|r| r.error.is_none()));
|
||||
let world = s.world_state();
|
||||
let world = s.world_state().unwrap();
|
||||
assert!(world.bodies.len() > 40, "{} bodies", world.bodies.len());
|
||||
assert!(world.bodies.iter().any(|b| b.name.contains("spoke")));
|
||||
}
|
||||
|
||||
@@ -40,8 +40,6 @@ pub fn one(args: &[String]) -> Result<(), String> {
|
||||
"steps": run.steps.iter().map(|s| s.kind).collect::<Vec<_>>(),
|
||||
"unsupported": run.unsupported,
|
||||
"rejected": run.rejected,
|
||||
"boolean_fallbacks": run.boolean_fallbacks,
|
||||
"boolean_reports_truncated": run.boolean_reports_truncated,
|
||||
"panics": PANICS.swap(0, std::sync::atomic::Ordering::SeqCst),
|
||||
});
|
||||
if run.outcome.is_bug() {
|
||||
@@ -191,23 +189,9 @@ fn summarise(results: &[Value], took: Duration) -> String {
|
||||
let mut boolean: BTreeMap<String, usize> = BTreeMap::new();
|
||||
let mut at_step: BTreeMap<String, usize> = BTreeMap::new();
|
||||
let mut unsupported: BTreeMap<String, usize> = BTreeMap::new();
|
||||
let mut fallbacks: BTreeMap<String, [u64; 2]> = BTreeMap::new();
|
||||
let mut truncated = [0usize; 2];
|
||||
let mut steps = 0;
|
||||
let (mut dp, mut bp) = (0, 0);
|
||||
for v in results {
|
||||
for (i, group) in ["design", "boolean"].into_iter().enumerate() {
|
||||
if v[group]["boolean_reports_truncated"].as_bool().unwrap_or(false) {
|
||||
truncated[i] += 1;
|
||||
}
|
||||
if let Some(counts) = v[group]["boolean_fallbacks"].as_object() {
|
||||
for (strategy, count) in counts {
|
||||
if let Some(total) = fallbacks.entry(strategy.clone()).or_default().get_mut(i) {
|
||||
*total = total.saturating_add(count.as_u64().unwrap_or(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dp += usize::from(v["design"]["panics"].as_u64().unwrap_or(0) > 0);
|
||||
bp += usize::from(v["boolean_panics"].as_u64().unwrap_or(0) > 0);
|
||||
let k = if v.get("timeout").is_some() {
|
||||
@@ -246,11 +230,6 @@ fn summarise(results: &[Value], took: Duration) -> String {
|
||||
for (k, c) in &boolean {
|
||||
s += &format!("- {k}: {}\n", pct(*c));
|
||||
}
|
||||
s += "\nSuccessful Boolean retries (events, including nested operations):\n";
|
||||
for (strategy, [designs, checks]) in &fallbacks {
|
||||
s += &format!("- {strategy}: designs {designs}, boolean checks {checks}\n");
|
||||
}
|
||||
s += &format!("- Capped reports: designs {}, boolean checks {}\n", truncated[0], truncated[1]);
|
||||
s
|
||||
}
|
||||
|
||||
@@ -259,15 +238,13 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn report_counts_retry_events_separately_and_marks_incomplete_logs() {
|
||||
fn report_counts_outcomes_and_timeouts() {
|
||||
let results = [
|
||||
json!({"design":{"outcome":{"kind":"ok"},"boolean_fallbacks":{"shifted_operands":2}},"boolean":{"outcome":"ok","boolean_fallbacks":{"shifted_operands":3},"boolean_reports_truncated":true}}),
|
||||
json!({"design":{"outcome":{"kind":"ok"},"boolean_fallbacks":{"shifted_operands":1}},"boolean":{"outcome":"ok"}}),
|
||||
json!({"design":{"outcome":{"kind":"ok"}},"boolean":{"outcome":"ok"}}),
|
||||
json!({"design":{"outcome":{"kind":"ok"}},"boolean":{"outcome":"ok"}}),
|
||||
json!({"timeout":90}),
|
||||
];
|
||||
let text = summarise(&results, Duration::ZERO);
|
||||
assert!(text.contains("shifted_operands: designs 3, boolean checks 3"), "{text}");
|
||||
assert!(text.contains("Capped reports: designs 0, boolean checks 1"), "{text}");
|
||||
assert!(text.contains("timeout: 1 (33.3 %)"), "{text}");
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+109
-23
@@ -10,12 +10,13 @@
|
||||
//! solidcraft-cli bench-assistant --connect 127.0.0.1:PORT [--token T] --base-url URL --model NAME [--tasks 1,2,5] [--repeat N]
|
||||
//! [--timeout-s 300] [--json out.json] [--markdown out.md]
|
||||
//! the standard Assistant model test (#4851) on a running app
|
||||
//! solidcraft-cli snapshot <script|design> [--in design|part.step] --out shot.png [--width W] [--height H] [--view iso|front|top|…|NAME | --camera JSON] [--no-grid] [--no-axes]
|
||||
//! solidcraft-cli snapshot <script|design> [--in design|part.step] --out shot.png [--width W] [--height H] [--view iso|front|top|…|NAME | --camera JSON] [--no-grid] [--no-axes] [--quality LEVEL]
|
||||
//! [--body NAME:opacity=0.3,color=#rrggbb]... [--tool NAME]... [--section z=27]
|
||||
//! solidcraft-cli exec <command> [json-params] run one command on an empty design
|
||||
//! solidcraft-cli commands the command registry as JSON
|
||||
//! solidcraft-cli recipe <recipe.json> translate an oracle recipe to a script
|
||||
//! solidcraft-cli oracle <case-dir>... replay recipes and compare with expected.json
|
||||
//! solidcraft-cli kernel-cases [--json] [--dumps=DIR] [--validation=MODE] <case-dir>... the kernel regression rig (tests/kernel-cases)
|
||||
//! solidcraft-cli mcp [--headless] [--in design|script] [--connect HOST:PORT [--token T]] [--allow-path DIR]... [--allow-any-path]
|
||||
//! [--param-check off|warn|strict]
|
||||
//! MCP server on stdio (docs/mcp.md)
|
||||
@@ -33,9 +34,11 @@ mod bench_agent;
|
||||
mod bench_assistant;
|
||||
mod crashes;
|
||||
mod fuzz;
|
||||
mod kernel_cases;
|
||||
mod logger;
|
||||
mod oracle;
|
||||
mod preview;
|
||||
mod profiling;
|
||||
mod recipe;
|
||||
mod render;
|
||||
mod spatial;
|
||||
@@ -58,19 +61,19 @@ struct Command {
|
||||
}
|
||||
|
||||
/// `run`'s lines in the usage.
|
||||
const USAGE_RUN: &str = " solidcraft-cli run <script.json|design.sc|part.step> [--in design.sc|part.step] [--out FILE]... [--save FILE] [--quiet] [--results] [--timings] [--profile] [--json-out FILE] [--allow-errors]\n [--param-check off|warn|strict]\n solidcraft-cli run --in <design.sc|part.step> [--out FILE]...\n (a timeline feature that fails fails the run and nothing is written; --allow-errors writes anyway)\n (--param-check checks each step against its command's contract: warn reports on stderr, strict stops the run)\n (--timings prints {timings: [{index, command, ms}], total_ms} for a script, with --results its results too;\n --profile adds each command's profile: {boolean_ms, tessellate_ms, other_ms}, the kernel's share)\n";
|
||||
const USAGE_RUN: &str = " solidcraft-cli run <script.json|design.sc|part.step> [--in design.sc|part.step] [--out FILE]... [--save FILE] [--quiet] [--results] [--timings] [--profile] [--profile-out trace.json] [--json-out FILE] [--allow-errors] [--validation each_step|each_feature|off]\n [--param-check off|warn|strict]\n solidcraft-cli run --in <design.sc|part.step> [--out FILE]...\n (a timeline feature that fails fails the run and nothing is written; --allow-errors writes anyway)\n (--validation: when SMLib's full check runs on the features computed (diagnostics.validation), default each_feature)\n (--param-check checks each step against its command's contract: warn reports on stderr, strict stops the run)\n (--timings prints {timings: [{index, command, ms}], total_ms} for a script, with --results its results too;\n --profile adds each command's profile: {boolean_ms, tessellate_ms, other_ms}, the kernel's share)\n (--profile also records where the time goes, SMLib's calls, kernel operations, features, commands and import stages,\n and prints the table to stderr: count, total, self time and longest per span; --profile-out FILE writes the spans\n as a Chrome trace for chrome://tracing or ui.perfetto.dev; SOLIDCRAFT_PROFILE=1 does the same for any command)\n";
|
||||
/// `eval`'s lines in the usage.
|
||||
const USAGE_EVAL: &str = " solidcraft-cli eval <script.json|design.sc|part.step> [--in design.sc|part.step] [--json-out FILE] (alias: inspect)\n solidcraft-cli eval --in <script.json|design.sc|part.step> [--json-out FILE]\n (with both inputs, runs the positional script on --in before measuring; does not automatically save the modified design)\n (--json-out FILE writes the JSON that run (with --results, else the measurement) and eval print to FILE instead of stdout,\n as plain UTF-8: a shell's > can re-encode it, e.g. Windows PowerShell adds a byte-order mark)\n";
|
||||
const USAGE_EVAL: &str = " solidcraft-cli eval <script.json|design.sc|part.step> [--in design.sc|part.step] [--json-out FILE] [--profile] [--profile-out trace.json] (alias: inspect)\n solidcraft-cli eval --in <script.json|design.sc|part.step> [--json-out FILE]\n (with both inputs, runs the positional script on --in before measuring; does not automatically save the modified design)\n (--json-out FILE writes the JSON that run (with --results, else the measurement) and eval print to FILE instead of stdout,\n as plain UTF-8: a shell's > can re-encode it, e.g. Windows PowerShell adds a byte-order mark)\n (--profile and --profile-out as for run)\n";
|
||||
/// `script`'s lines in the usage.
|
||||
const USAGE_SCRIPT: &str = " solidcraft-cli script <design.sc|script.json> [--out script.json]\n (the design's history as a command script that eval and run replay)\n";
|
||||
/// `bench-open`'s lines in the usage.
|
||||
const USAGE_BENCH_OPEN: &str = " solidcraft-cli bench-open <design.sc | part.step> [--parse-only] [--save OUT.sc] [--display LEVEL [--edit NAME EXPR]] [--ops script.json]\n (--ops: after opening, run each command of the script and undo it, timing the command, the\n evaluations it caused and the view it leaves: placing and meshing what changed)\n";
|
||||
const USAGE_BENCH_OPEN: &str = " solidcraft-cli bench-open <design.sc | part.step> [--parse-only] [--save OUT.sc] [--display LEVEL [--edit NAME EXPR]] [--ops script.json] [--profile] [--profile-out trace.json]\n (--profile and --profile-out as for run)\n (--ops: after opening, run each command of the script and undo it, timing the command, the\n evaluations it caused and the view it leaves: placing and meshing what changed)\n";
|
||||
/// `bench-agent`'s lines in the usage.
|
||||
const USAGE_BENCH_AGENT: &str = " solidcraft-cli bench-agent [--scale F] [--save design.sc] [--script-out script.json]\n";
|
||||
/// `bench-assistant`'s lines in the usage.
|
||||
const USAGE_BENCH_ASSISTANT: &str = " solidcraft-cli bench-assistant --connect 127.0.0.1:PORT [--token T] --base-url URL --model NAME [--tasks 1,2,5] [--repeat N] [--timeout-s 300] [--json out.json] [--markdown out.md]\n";
|
||||
/// `snapshot`'s lines in the usage.
|
||||
const USAGE_SNAPSHOT: &str = " solidcraft-cli snapshot <script|design> [--in design.sc|part.step] --out shot.png [--width W] [--height H] [--view NAME | --camera JSON] [--no-grid] [--no-axes]\n [--body NAME:opacity=0.3,color=#rrggbb]... [--tool NAME]... [--section z=27]\n solidcraft-cli snapshot --in <design.sc|part.step> --out shot.png ...\n (as with run, --in starts from a design and the script runs on top of it; an option snapshot doesn't take is an error;\n --view: iso, front, back, top, bottom, left, right, or the name of a view saved in the design with view.save;\n --camera: {target: [x,y,z], yaw, pitch, distance, fov?} in mm and radians, as the MCP screenshot takes it (docs/mcp.md);\n --no-grid and --no-axes leave the ground grid (on Z 0) and the origin's axes out of the picture;\n --body: how one body is drawn, as appearance.assign takes it: opacity 0-1 (below 1 is see-through, so what is inside\n it shows) and color #rrggbb, either or both, once per body;\n --tool: draw this body solid in the tool colour and every other body see-through, so a cut's or a join's tool body\n is visible inside the material it meets; a later --body overrides it;\n --section: cut the model as inspect.section does, so the inside shows: z=27 (the plane through that world\n coordinate, keeping the smaller side), add ,flip to keep the other side, or XY, XZ, YZ, or its own JSON)\n";
|
||||
const USAGE_SNAPSHOT: &str = " solidcraft-cli snapshot <script|design> [--in design.sc|part.step] --out shot.png [--width W] [--height H] [--view NAME | --camera JSON] [--no-grid] [--no-axes]\n [--body NAME:opacity=0.3,color=#rrggbb]... [--tool NAME]... [--section z=27] [--quality coarse|normal|fine|ultra]\n solidcraft-cli snapshot --in <design.sc|part.step> --out shot.png ...\n (as with run, --in starts from a design and the script runs on top of it; an option snapshot doesn't take is an error;\n --view: iso, front, back, top, bottom, left, right, or the name of a view saved in the design with view.save;\n --camera: {target: [x,y,z], yaw, pitch, distance, fov?} in mm and radians, as the MCP screenshot takes it (docs/mcp.md);\n --no-grid and --no-axes leave the ground grid (on Z 0) and the origin's axes out of the picture;\n --body: how one body is drawn, as appearance.assign takes it: opacity 0-1 (below 1 is see-through, so what is inside\n it shows) and color #rrggbb, either or both, once per body;\n --tool: draw this body solid in the tool colour and every other body see-through, so a cut's or a join's tool body\n is visible inside the material it meets; a later --body overrides it;\n --section: cut the model as inspect.section does, so the inside shows: z=27 (the plane through that world\n coordinate, keeping the smaller side), add ,flip to keep the other side, or XY, XZ, YZ, or its own JSON;\n --quality: the display quality the bodies are meshed at, as the app's setting (default normal))\n";
|
||||
/// `render`'s lines in the usage.
|
||||
const USAGE_RENDER: &str = " solidcraft-cli render <script|design> --out image.png [--setup NAME] [--width W] [--height H] [--transparent] [--overwrite] [--timeout SECONDS]\n [--progressive] [--samples N] [--max-depth N] [--seed N] [--exposure EV] [--lighting FILE.json] [--environment FILE.json] [--tone-map reinhard|aces_fitted]\n";
|
||||
/// `exec`'s lines in the usage.
|
||||
@@ -81,6 +84,8 @@ const USAGE_COMMANDS: &str = " solidcraft-cli commands\n";
|
||||
const USAGE_RECIPE: &str = " solidcraft-cli recipe <recipe.json>\n";
|
||||
/// `oracle`'s lines in the usage.
|
||||
const USAGE_ORACLE: &str = " solidcraft-cli oracle <case-dir>... [--json]\n";
|
||||
/// `kernel-cases`'s lines in the usage.
|
||||
const USAGE_KERNEL_CASES: &str = " solidcraft-cli kernel-cases <case-dir>... [--json] [--dumps=DIR] [--validation=each_step|each_feature|off]\n (the kernel regression rig, tests/kernel-cases, on SMLib; --dumps: each case's\n failing SMLib calls leave their inputs for replay in DIR/<area>-<case>; --validation: when SMLib's\n full check runs (diagnostics.validation), default each_feature)\n";
|
||||
/// `fuzz`'s lines in the usage.
|
||||
const USAGE_FUZZ: &str =
|
||||
" solidcraft-cli fuzz [--from N] [--count N] [--jobs J] [--timeout S] [--steps N] [--minimise] [--out DIR] [--report FILE]\n";
|
||||
@@ -118,6 +123,7 @@ const COMMANDS: &[Command] = &[
|
||||
Command { name: "commands", usage: USAGE_COMMANDS, listed: true },
|
||||
Command { name: "recipe", usage: USAGE_RECIPE, listed: true },
|
||||
Command { name: "oracle", usage: USAGE_ORACLE, listed: true },
|
||||
Command { name: "kernel-cases", usage: USAGE_KERNEL_CASES, listed: true },
|
||||
Command { name: "fuzz", usage: USAGE_FUZZ, listed: true },
|
||||
Command { name: "fuzz-one", usage: USAGE_FUZZ_ONE, listed: false },
|
||||
Command { name: "mcp", usage: USAGE_MCP, listed: true },
|
||||
@@ -133,6 +139,19 @@ const COMMANDS: &[Command] = &[
|
||||
fn main() -> ExitCode {
|
||||
let mut args: Vec<String> = std::env::args().skip(1).collect();
|
||||
logger::init(&mut args);
|
||||
// `--profile` / `--profile-out` (run, eval, bench-open) or SOLIDCRAFT_PROFILE (any command):
|
||||
// record from here, and print the table when the command ends.
|
||||
let profiling = match args.first().map(String::as_str) {
|
||||
Some("run" | "eval" | "inspect" | "bench-open") => profiling::start(&args[1..]),
|
||||
_ => profiling::start(&[]),
|
||||
};
|
||||
let profiling = match profiling {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
eprintln!("error: {e}");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
};
|
||||
if let Err(e) = max_file_size(&mut args) {
|
||||
eprintln!("error: {e}");
|
||||
return ExitCode::FAILURE;
|
||||
@@ -170,9 +189,14 @@ fn main() -> ExitCode {
|
||||
recipe::to_script(&v).map(|s| println!("{}", pretty(&s)))
|
||||
}),
|
||||
Some("oracle") => oracle::run(&args[1..]),
|
||||
Some("kernel-cases") => kernel_cases::run(&args[1..]),
|
||||
Some("fuzz") => fuzz::run(&args[1..]),
|
||||
Some("fuzz-one") => fuzz::one(&args[1..]),
|
||||
Some("mcp") => cmd_mcp(&args[1..]),
|
||||
Some("mcp") => {
|
||||
let result = cmd_mcp(&args[1..]);
|
||||
solidcraft_mcp::cleanup_scratch_dir();
|
||||
result
|
||||
}
|
||||
Some("crashes") => crashes::run(&args[1..]),
|
||||
Some("icon") => cmd_icon(&args[1..]),
|
||||
Some("licences" | "licenses") => {
|
||||
@@ -202,6 +226,11 @@ fn main() -> ExitCode {
|
||||
}
|
||||
_ => Err(usage()),
|
||||
};
|
||||
// The table also when the command failed: where it spent its time before failing.
|
||||
let r = match (r, profiling.map(profiling::Profiling::finish)) {
|
||||
(Ok(()), Some(Err(e))) => Err(e),
|
||||
(r, _) => r,
|
||||
};
|
||||
match r {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(e) => {
|
||||
@@ -282,8 +311,15 @@ pub fn load(path: &str) -> Result<Session, String> {
|
||||
/// `load`, with scripts and recipes run at the parameter-check level `check`, which the
|
||||
/// session keeps.
|
||||
fn load_checked(path: &str, check: solidcraft_engine::schema::ParamCheck) -> Result<Session, String> {
|
||||
load_with(path, check, solidcraft_engine::kernel::Validation::default())
|
||||
}
|
||||
|
||||
/// `load_checked`, the features computed under `validation` (`diagnostics.validation`), which
|
||||
/// the session keeps.
|
||||
fn load_with(path: &str, check: solidcraft_engine::schema::ParamCheck, validation: solidcraft_engine::kernel::Validation) -> Result<Session, String> {
|
||||
let mut s = Session::default();
|
||||
s.param_check = check;
|
||||
s.set_validation(validation);
|
||||
if solidcraft_engine::io::is_step_path(path) || solidcraft_engine::io::is_mesh_path(path) || solidcraft_engine::io::is_point_cloud_path(path) {
|
||||
s.execute("doc.open", &json!({"path": path})).map_err(|e| e.to_string())?;
|
||||
return Ok(s);
|
||||
@@ -291,7 +327,7 @@ fn load_checked(path: &str, check: solidcraft_engine::schema::ParamCheck) -> Res
|
||||
let bytes = solidcraft_engine::io::limits::read(path, solidcraft_engine::io::limits::Kind::Design).map_err(|e| format!("{path}: {e}"))?;
|
||||
if solidcraft_engine::io::is_design_container(&bytes) {
|
||||
let doc = solidcraft_engine::io::read_design(&bytes).map_err(|e| format!("{path}: {e}"))?;
|
||||
let mut session = Session::opened_at(doc, None, Some(path));
|
||||
let mut session = Session::opened_at_validating(doc, None, Some(path), validation);
|
||||
session.param_check = check;
|
||||
return Ok(session);
|
||||
}
|
||||
@@ -355,6 +391,16 @@ fn positional(args: &[String]) -> Option<&String> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Is `path` a command script (or a recipe), not a design: JSON that is no saved design.
|
||||
fn is_script(path: &str) -> bool {
|
||||
let Ok(bytes) = solidcraft_engine::io::vfs::read(path, solidcraft_engine::io::MAX_DESIGN_BYTES) else { return false };
|
||||
if solidcraft_engine::io::is_design_container(&bytes) {
|
||||
return false;
|
||||
}
|
||||
serde_json::from_slice::<Value>(&bytes)
|
||||
.is_ok_and(|v| !v.get("format").and_then(Value::as_str).is_some_and(|f| solidcraft_engine::doc::format::version_of(f).is_some()))
|
||||
}
|
||||
|
||||
/// `--json-out FILE`: where the JSON a command prints goes instead of stdout.
|
||||
fn json_out(args: &[String]) -> Result<Option<&str>, String> {
|
||||
if !args.iter().any(|a| a == "--json-out") {
|
||||
@@ -391,24 +437,31 @@ fn cmd_run(args: &[String]) -> Result<(), String> {
|
||||
known_args(
|
||||
"run",
|
||||
args,
|
||||
&["--in", "--out", "--save", "--json-out", "--param-check"],
|
||||
&["--in", "--out", "--save", "--json-out", "--param-check", "--profile-out", "--validation"],
|
||||
&["--quiet", "--results", "--timings", "--profile", "--allow-errors"],
|
||||
true,
|
||||
)?;
|
||||
let validation = validation_flag("run", args)?;
|
||||
let base = flag(args, "--in");
|
||||
let path = positional(args);
|
||||
if path.is_none() && base.is_none() {
|
||||
return Err(usage());
|
||||
}
|
||||
let want_results = args.iter().any(|a| a == "--results");
|
||||
// `--profile` times each command and splits out its kernel time; it implies `--timings`.
|
||||
let profile = args.iter().any(|a| a == "--profile");
|
||||
// `--profile` records the profiler's spans (the table goes to stderr, `profiling.rs`); for a
|
||||
// script it also times each command and splits out its kernel time, implying `--timings`.
|
||||
let script = path.is_some_and(|p| is_script(p));
|
||||
let profile = script && args.iter().any(|a| a == "--profile" || a == "--profile-out");
|
||||
let timings = profile || args.iter().any(|a| a == "--timings");
|
||||
let (mut s, results) = if want_results || timings {
|
||||
// Collect every command's result or time (scripts only), then process requested files too.
|
||||
let mut s = match base {
|
||||
Some(b) => load_checked(b, check)?,
|
||||
None => Session::default(),
|
||||
Some(b) => load_with(b, check, validation)?,
|
||||
None => {
|
||||
let mut s = Session::default();
|
||||
s.set_validation(validation);
|
||||
s
|
||||
}
|
||||
};
|
||||
s.param_check = check;
|
||||
let needs = if want_results { "--results needs a script" } else { "--timings needs a script" };
|
||||
@@ -425,7 +478,7 @@ fn cmd_run(args: &[String]) -> Result<(), String> {
|
||||
};
|
||||
(s, Some(out))
|
||||
} else {
|
||||
(open(base, path, check)?, None)
|
||||
(open_validating(base, path, check, validation)?, None)
|
||||
};
|
||||
// A feature that fails leaves the bodies as they were before it (a parameter change can
|
||||
// break a feature without failing the command that made it): writing them would hand out
|
||||
@@ -452,18 +505,38 @@ fn cmd_run(args: &[String]) -> Result<(), String> {
|
||||
/// The session `run`, `eval` and `snapshot` start from: `--in` (a design or STEP file) with the
|
||||
/// positional script run on top of it, or either one alone, with scripts run at `check`.
|
||||
fn open(base: Option<&str>, path: Option<&String>, check: solidcraft_engine::schema::ParamCheck) -> Result<Session, String> {
|
||||
open_validating(base, path, check, solidcraft_engine::kernel::Validation::default())
|
||||
}
|
||||
|
||||
/// [`open`], the features computed under `validation`.
|
||||
fn open_validating(
|
||||
base: Option<&str>,
|
||||
path: Option<&String>,
|
||||
check: solidcraft_engine::schema::ParamCheck,
|
||||
validation: solidcraft_engine::kernel::Validation,
|
||||
) -> Result<Session, String> {
|
||||
let load = |p: &str| load_with(p, check, validation);
|
||||
match (base, path) {
|
||||
(Some(b), Some(p)) => {
|
||||
let mut s = load_checked(b, check)?;
|
||||
let mut s = load(b)?;
|
||||
run_script_warning(&mut s, p, &read_json(p)?)?;
|
||||
Ok(s)
|
||||
}
|
||||
(Some(b), None) => load_checked(b, check),
|
||||
(None, Some(p)) => load_checked(p, check),
|
||||
(Some(b), None) => load(b),
|
||||
(None, Some(p)) => load(p),
|
||||
(None, None) => Err(usage()),
|
||||
}
|
||||
}
|
||||
|
||||
/// `--validation MODE` (`diagnostics.validation`): the default, each_feature, when not given.
|
||||
fn validation_flag(cmd: &str, args: &[String]) -> Result<solidcraft_engine::kernel::Validation, String> {
|
||||
match flag(args, "--validation") {
|
||||
Some(m) => solidcraft_engine::kernel::Validation::parse(m)
|
||||
.ok_or_else(|| format!("{cmd}: --validation must be each_step, each_feature or off, not `{m}`")),
|
||||
None => Ok(solidcraft_engine::kernel::Validation::default()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Refuses an option `cmd` doesn't take, and one missing its value: a typo, or another
|
||||
/// command's option, would otherwise be ignored without a word. `positionals` says whether the
|
||||
/// command takes a word of its own (a file, a seed): `fuzz` takes none, and would start a run.
|
||||
@@ -501,7 +574,7 @@ fn timeline_ok(s: &Session) -> Result<(), String> {
|
||||
|
||||
fn cmd_eval(args: &[String]) -> Result<(), String> {
|
||||
let json_out = json_out(args)?;
|
||||
known_args("eval", args, &["--in", "--json-out"], &[], true)?;
|
||||
known_args("eval", args, &["--in", "--json-out", "--profile-out"], &["--profile"], true)?;
|
||||
let mut s = open(flag(args, "--in"), positional(args), solidcraft_engine::schema::ParamCheck::Off)?;
|
||||
let m = s.execute("inspect.measure", &json!({})).map_err(|e| e.to_string())?;
|
||||
let d = s.execute("document.inspect", &json!({})).map_err(|e| e.to_string())?;
|
||||
@@ -583,7 +656,7 @@ fn cmd_snapshot(args: &[String]) -> Result<(), String> {
|
||||
known_args(
|
||||
"snapshot",
|
||||
args,
|
||||
&["--in", "--out", "--width", "--height", "--view", "--camera", "--body", "--tool", "--section"],
|
||||
&["--in", "--out", "--width", "--height", "--view", "--camera", "--body", "--tool", "--section", "--quality"],
|
||||
&["--no-grid", "--no-axes"],
|
||||
true,
|
||||
)?;
|
||||
@@ -596,6 +669,16 @@ fn cmd_snapshot(args: &[String]) -> Result<(), String> {
|
||||
Some(x) => x.parse().ok().filter(|n| (16..=8192).contains(n)).ok_or_else(|| format!("{name} must be 16 to 8192 pixels, not `{x}`")),
|
||||
};
|
||||
let (w, h) = (size("--width", 1280)?, size("--height", 800)?);
|
||||
// The display quality the bodies are meshed at, as the app's Display Quality setting: set
|
||||
// before the design opens, so every display plan is made at it.
|
||||
if let Some(q) = flag(args, "--quality") {
|
||||
use solidcraft_engine::doc::display_quality::{DisplayQuality, set_current};
|
||||
let level = DisplayQuality::parse(q).ok_or_else(|| {
|
||||
let ids: Vec<&str> = DisplayQuality::ALL.iter().map(|q| q.id()).collect();
|
||||
format!("--quality must be one of {}, not `{q}`", ids.join(", "))
|
||||
})?;
|
||||
set_current(level);
|
||||
}
|
||||
let mut s = open(flag(args, "--in"), positional(args), solidcraft_engine::schema::ParamCheck::Off)?;
|
||||
// How the bodies are drawn and where the model is cut, through the commands the app's
|
||||
// Appearance dialog and Section Analysis run (`preview`).
|
||||
@@ -615,9 +698,12 @@ fn cmd_snapshot(args: &[String]) -> Result<(), String> {
|
||||
}
|
||||
(None, None) => {}
|
||||
}
|
||||
let cam = s.camera.unwrap_or_else(|| solidcraft_engine::view::home_camera(&s));
|
||||
let cam = match s.camera {
|
||||
Some(cam) => cam,
|
||||
None => solidcraft_engine::view::home_camera(&s).map_err(|e| e.to_string())?,
|
||||
};
|
||||
let guides = Guides { grid: !args.iter().any(|a| a == "--no-grid"), axes: !args.iter().any(|a| a == "--no-axes") };
|
||||
let scene = solidcraft_engine::view::scene_with_guides(&s, &cam, s.section, guides);
|
||||
let scene = solidcraft_engine::view::scene_with_guides(&s, &cam, s.section, guides).map_err(|e| e.to_string())?;
|
||||
let png = solidcraft_engine::capture::png(&scene, &cam, w, h)?;
|
||||
std::fs::write(out, &png.bytes).map_err(|e| format!("{out}: {e}"))?;
|
||||
let mut reply = json!({"path": out, "width": w, "height": h, "bytes": png.bytes.len(), "renderer": png.renderer});
|
||||
@@ -846,7 +932,7 @@ fn bench_ops(mut s: Session, script: &str) -> Result<Session, String> {
|
||||
let r = s.execute(id, p).map_err(|e| format!("{id}: {e}"))?;
|
||||
let ms = t.elapsed().as_secs_f64() * 1000.0;
|
||||
let t = std::time::Instant::now();
|
||||
s.world_state().warm_meshes(|| true);
|
||||
s.world_state().map_err(|e| e.to_string())?.warm_meshes(|| true);
|
||||
let view_ms = t.elapsed().as_secs_f64() * 1000.0;
|
||||
println!("{}", json!({"step": "op", "command": id, "ms": ms, "view_ms": view_ms}));
|
||||
Ok(r)
|
||||
@@ -902,7 +988,7 @@ pub(crate) fn cmd_bench_open(args: &[String]) -> Result<(), String> {
|
||||
println!("{}", json!({"step": "feature", "id": r.id, "name": r.name, "ms": r.ms, "error": r.error}));
|
||||
}
|
||||
let t = std::time::Instant::now();
|
||||
let st = s.world_state();
|
||||
let st = s.world_state().map_err(|e| e.to_string())?;
|
||||
let place_ms = t.elapsed().as_secs_f64() * 1000.0;
|
||||
// As the desktop app prepares a design it opened.
|
||||
st.warm_meshes(|| true);
|
||||
@@ -972,7 +1058,7 @@ pub(crate) fn cmd_bench_open(args: &[String]) -> Result<(), String> {
|
||||
let t = std::time::Instant::now();
|
||||
s.execute("parameters.change", &json!({"name": name, "expression": expr})).map_err(|e| e.to_string())?;
|
||||
let eval_ms = t.elapsed().as_secs_f64() * 1000.0;
|
||||
let st = s.world_state();
|
||||
let st = s.world_state().map_err(|e| e.to_string())?;
|
||||
let t = std::time::Instant::now();
|
||||
st.warm_meshes(|| true);
|
||||
let mesh_ms = t.elapsed().as_secs_f64() * 1000.0;
|
||||
|
||||
@@ -13,28 +13,28 @@ use crate::{read_json, recipe};
|
||||
/// Default relative tolerance for volume and area (our curved-face tessellation is within ~2e-4).
|
||||
pub const REL_TOL: f64 = 1e-3;
|
||||
/// Default tolerance for centre of mass and bounding box, as a fraction of the bbox diagonal.
|
||||
const ABS_FRAC: f64 = 1e-3;
|
||||
pub(crate) const ABS_FRAC: f64 = 1e-3;
|
||||
/// Chord tolerance used to triangulate our part for the surface-distance check (mm).
|
||||
const MESH_TOL: f64 = 0.01;
|
||||
/// Relative tolerance on the principal second moments of the two triangulations.
|
||||
const INERTIA_REL: f64 = 2e-3;
|
||||
|
||||
/// What SolidCraft built: totals over every body.
|
||||
struct Got {
|
||||
body_count: usize,
|
||||
volume: f64,
|
||||
area: f64,
|
||||
com: [f64; 3],
|
||||
bbox: ([f64; 3], [f64; 3]),
|
||||
faces: u64,
|
||||
pub(crate) struct Got {
|
||||
pub(crate) body_count: usize,
|
||||
pub(crate) volume: f64,
|
||||
pub(crate) area: f64,
|
||||
pub(crate) com: [f64; 3],
|
||||
pub(crate) bbox: ([f64; 3], [f64; 3]),
|
||||
pub(crate) faces: u64,
|
||||
}
|
||||
|
||||
fn v3(v: &Value) -> Option<[f64; 3]> {
|
||||
pub(crate) fn v3(v: &Value) -> Option<[f64; 3]> {
|
||||
let a = v.as_array()?;
|
||||
Some([a.first()?.as_f64()?, a.get(1)?.as_f64()?, a.get(2)?.as_f64()?])
|
||||
}
|
||||
|
||||
fn totals(m: &Value) -> Got {
|
||||
pub(crate) fn totals(m: &Value) -> Got {
|
||||
let bodies = m["bodies"].as_array().cloned().unwrap_or_default();
|
||||
let (mut volume, mut area, mut faces) = (0.0, 0.0, 0);
|
||||
let mut moment = [0.0; 3];
|
||||
@@ -57,21 +57,21 @@ fn totals(m: &Value) -> Got {
|
||||
Got { body_count: bodies.len(), volume, area, com, bbox: (lo, hi), faces }
|
||||
}
|
||||
|
||||
struct Checks {
|
||||
list: Vec<Value>,
|
||||
pass: bool,
|
||||
pub(crate) struct Checks {
|
||||
pub(crate) list: Vec<Value>,
|
||||
pub(crate) pass: bool,
|
||||
}
|
||||
|
||||
impl Checks {
|
||||
fn push(&mut self, what: &str, got: Value, want: Value, ok: bool) {
|
||||
pub(crate) fn push(&mut self, what: &str, got: Value, want: Value, ok: bool) {
|
||||
self.pass &= ok;
|
||||
self.list.push(json!({"check": what, "got": got, "want": want, "ok": ok}));
|
||||
}
|
||||
fn rel(&mut self, what: &str, g: f64, w: Option<f64>, rel: f64) {
|
||||
pub(crate) fn rel(&mut self, what: &str, g: f64, w: Option<f64>, rel: f64) {
|
||||
let ok = w.is_some_and(|w| g.is_finite() && (g - w).abs() <= rel * w.abs().max(1.0));
|
||||
self.push(what, json!(g), json!(w), ok);
|
||||
}
|
||||
fn vec(&mut self, what: &str, g: [f64; 3], w: Option<[f64; 3]>, tol: f64) {
|
||||
pub(crate) fn vec(&mut self, what: &str, g: [f64; 3], w: Option<[f64; 3]>, tol: f64) {
|
||||
let ok = w.is_some_and(|w| g.iter().zip(w).all(|(g, w)| g.is_finite() && (g - w).abs() <= tol));
|
||||
self.push(what, json!(g), json!(w), ok);
|
||||
}
|
||||
@@ -186,8 +186,8 @@ fn step_export_check(s: &solidcraft_engine::Session, volume: f64) -> (bool, Stri
|
||||
/// Our part as a triangle soup (curved faces refined onto the surface).
|
||||
fn our_mesh(s: &solidcraft_engine::Session) -> Result<Vec<Tri>, String> {
|
||||
let mut ours: Vec<Tri> = Vec::new();
|
||||
for b in &s.world_state().bodies {
|
||||
let m = b.body.tessellate_with(MESH_TOL, true).map_err(|e| e.to_string())?;
|
||||
for b in &s.world_state().map_err(|e| e.to_string())?.bodies {
|
||||
let m = b.body.mesh(&solidcraft_engine::kernel::MeshTolerance::chord(MESH_TOL)).map_err(|e| e.to_string())?;
|
||||
let p = |i: u32| m.positions.get(i as usize).map(|v: &Vec3| [v.x, v.y, v.z]);
|
||||
for t in &m.triangles {
|
||||
if let (Some(a), Some(b), Some(c)) = (p(t[0]), p(t[1]), p(t[2])) {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
//! `--profile` and `--profile-out FILE` for `run`, `eval` and `bench-open` (and
|
||||
//! `SOLIDCRAFT_PROFILE=1` for any command): record where the time goes, the kernel's calls to the
|
||||
//! commands, print the table to stderr when the command ends (stdout stays the command's JSON)
|
||||
//! and write the spans as a Chrome trace for chrome://tracing or Perfetto.
|
||||
|
||||
use solidcraft_engine::profile;
|
||||
|
||||
/// Rows the table prints.
|
||||
const TOP: usize = 30;
|
||||
|
||||
/// A recording that prints its table when finished.
|
||||
pub struct Profiling {
|
||||
trace: Option<String>,
|
||||
}
|
||||
|
||||
/// Start recording when `args` (after the command) ask for it, or `SOLIDCRAFT_PROFILE` does.
|
||||
pub fn start(args: &[String]) -> Result<Option<Profiling>, String> {
|
||||
let trace = match args.iter().position(|a| a == "--profile-out") {
|
||||
Some(i) => match args.get(i + 1) {
|
||||
Some(f) if !f.starts_with("--") => Some(f.clone()),
|
||||
_ => return Err("--profile-out needs a FILE".into()),
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
let asked = trace.is_some() || args.iter().any(|a| a == "--profile");
|
||||
if !asked && !solidcraft_engine::timing::profile_from_env() {
|
||||
return Ok(None);
|
||||
}
|
||||
if !profile::COMPILED {
|
||||
return Err("this solidcraft-cli was built without the profiler (its `profile` feature)".into());
|
||||
}
|
||||
profile::reset();
|
||||
profile::set_enabled(true);
|
||||
Ok(Some(Profiling { trace }))
|
||||
}
|
||||
|
||||
impl Profiling {
|
||||
/// Stop recording, print the table on stderr and write the trace.
|
||||
pub fn finish(self) -> Result<(), String> {
|
||||
profile::set_enabled(false);
|
||||
eprint!("{}", profile::report().table(TOP));
|
||||
if let Some(path) = self.trace {
|
||||
let mut bytes = Vec::new();
|
||||
profile::write_chrome_trace(&mut bytes).map_err(|e| format!("{path}: {e}"))?;
|
||||
std::fs::write(&path, bytes).map_err(|e| format!("{path}: {e}"))?;
|
||||
eprintln!("profile: trace written to {path} (open it in chrome://tracing or ui.perfetto.dev)");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -196,7 +196,7 @@ fn round3(x: f64) -> f64 {
|
||||
|
||||
/// Every action on every entity of the design's final bodies, in a fixed order.
|
||||
fn actions(s: &Session) -> Vec<Action> {
|
||||
let st = s.world_state();
|
||||
let st = s.world_state().unwrap();
|
||||
let placed = s.placed_document();
|
||||
let paths: BTreeMap<String, Vec<u64>> =
|
||||
placed.body_frames_with_source(&s.model.state()).into_iter().map(|(world, _, _, path, _)| (world, path)).collect();
|
||||
@@ -1338,7 +1338,9 @@ fn a_fillet_or_chamfer_on_one_edge_ending_at_a_cylinder() {
|
||||
fn a_round_on_a_knuckle_cap_circle() {
|
||||
for (cmd, size, removed) in [("solid.fillet", "radius", (2.4, 2.8)), ("solid.chamfer", "distance", (5.6, 6.3))] {
|
||||
let mut s = load(&root().join("tests/oracle/63-hinge-knuckle/recipe.json")).unwrap();
|
||||
let volume = |s: &Session| s.model.state().bodies.iter().map(|b| b.body.tessellate(0.001).unwrap().measure().volume).sum::<f64>();
|
||||
let volume = |s: &Session| {
|
||||
s.model.state().bodies.iter().map(|b| b.body.mesh(&solidcraft_kernel::MeshTolerance::chord(0.001)).unwrap().measure().volume).sum::<f64>()
|
||||
};
|
||||
let before = volume(&s);
|
||||
s.execute(cmd, &json!({"edges": [[40.0, 29.837_722, 6.449_490]], size: 0.728})).unwrap_or_else(|e| panic!("{cmd}: {e}"));
|
||||
check(&s, 0, &BTreeMap::new()).unwrap_or_else(|(kind, e)| panic!("{cmd}: {kind}: {e}"));
|
||||
@@ -1358,7 +1360,9 @@ fn a_round_on_a_knuckle_cap_circle() {
|
||||
#[test]
|
||||
fn a_hair_thin_hole_on_a_ledge_beside_a_loft_cut() {
|
||||
let mut s = load(&root().join("examples/regressions/loft_cut_beside_round_hole.json")).unwrap();
|
||||
let volume = |s: &Session| s.model.state().bodies.iter().map(|b| b.body.tessellate_with(0.005, true).unwrap().measure().volume).sum::<f64>();
|
||||
let volume = |s: &Session| {
|
||||
s.model.state().bodies.iter().map(|b| b.body.mesh(&solidcraft_kernel::MeshTolerance::chord(0.005)).unwrap().measure().volume).sum::<f64>()
|
||||
};
|
||||
let before = volume(&s);
|
||||
s.execute("solid.hole", &json!({"diameter": 0.133, "position": [357.22, -150.993_333, 730.0]})).unwrap();
|
||||
check(&s, 0, &BTreeMap::new()).unwrap_or_else(|(kind, e)| panic!("{kind}: {e}"));
|
||||
@@ -1375,7 +1379,9 @@ fn a_hair_thin_hole_on_a_ledge_beside_a_loft_cut() {
|
||||
#[test]
|
||||
fn a_small_hole_through_the_wall_of_a_plastic_rest() {
|
||||
let mut s = load(&root().join("apps/solidcraft-cli/tests/corpus/modern-plastic.solidcraft")).unwrap();
|
||||
let volume = |s: &Session| s.model.state().bodies.iter().map(|b| b.body.tessellate_with(0.002, true).unwrap().measure().volume).sum::<f64>();
|
||||
let volume = |s: &Session| {
|
||||
s.model.state().bodies.iter().map(|b| b.body.mesh(&solidcraft_kernel::MeshTolerance::chord(0.002)).unwrap().measure().volume).sum::<f64>()
|
||||
};
|
||||
let before = volume(&s);
|
||||
s.execute("solid.hole", &json!({"diameter": 0.8, "position": [63.069_841_5, 28.333_333, 4.0]})).unwrap();
|
||||
check(&s, 0, &BTreeMap::new()).unwrap_or_else(|(kind, e)| panic!("{kind}: {e}"));
|
||||
@@ -1390,7 +1396,9 @@ fn a_small_hole_through_the_wall_of_a_plastic_rest() {
|
||||
#[test]
|
||||
fn a_hole_square_to_a_45_degree_flange() {
|
||||
let mut s = load(&root().join("tests/oracle/70-sm-flange-45/recipe.json")).unwrap();
|
||||
let volume = |s: &Session| s.model.state().bodies.iter().map(|b| b.body.tessellate_with(0.002, true).unwrap().measure().volume).sum::<f64>();
|
||||
let volume = |s: &Session| {
|
||||
s.model.state().bodies.iter().map(|b| b.body.mesh(&solidcraft_kernel::MeshTolerance::chord(0.002)).unwrap().measure().volume).sum::<f64>()
|
||||
};
|
||||
let before = volume(&s);
|
||||
s.execute("solid.hole", &json!({"diameter": 3.448, "position": [66.666_667, -4.511_845, 4.511_845]})).unwrap();
|
||||
check(&s, 0, &BTreeMap::new()).unwrap_or_else(|(kind, e)| panic!("{kind}: {e}"));
|
||||
@@ -1728,7 +1736,7 @@ fn the_ribbed_lid_builds_the_same_body_every_time() {
|
||||
st.bodies
|
||||
.iter()
|
||||
.map(|b| {
|
||||
let mesh = b.body.tessellate(0.1).unwrap();
|
||||
let mesh = b.body.mesh(&solidcraft_kernel::MeshTolerance::chord(0.1)).unwrap();
|
||||
let bits = mesh.positions.iter().fold(0u64, |acc, p| {
|
||||
acc.rotate_left(7).wrapping_add(p.x.to_bits() ^ p.y.to_bits().rotate_left(21) ^ p.z.to_bits().rotate_left(42))
|
||||
});
|
||||
|
||||
@@ -26,8 +26,8 @@ fn assert_two_through_holes(session: &Session, stage: &str) {
|
||||
let expected = 2.0 * PI * 4.0_f64.powi(2) * 6.0;
|
||||
assert!((removed - expected).abs() < expected * 1e-3, "{stage}: the two holes remove {removed} mm³, expected {expected} mm³");
|
||||
|
||||
let before_mesh = uncut.tessellate(0.01).unwrap();
|
||||
let after_mesh = bracket.tessellate(0.01).unwrap();
|
||||
let before_mesh = uncut.mesh(&solidcraft_kernel::MeshTolerance::chord(0.01)).unwrap();
|
||||
let after_mesh = bracket.mesh(&solidcraft_kernel::MeshTolerance::chord(0.01)).unwrap();
|
||||
let down = Vec3::new(0.0, 0.0, -1.0);
|
||||
for y in [-15.0, -45.0] {
|
||||
let center = Vec3::new(25.0, y, 1.0);
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
# Design file corpus
|
||||
|
||||
Designs saved by older SolidCraft builds. `tests/design_corpus.rs` opens each one with the
|
||||
current build and checks that it evaluates as it did when saved (the `.expected.json` next to
|
||||
it is `solidcraft-cli eval` of that file by the build that saved it: bodies, volumes, areas,
|
||||
world bounding boxes), that it is upgraded to the current file format, and that damaged and
|
||||
hostile variants of it never panic.
|
||||
current build and checks that it is upgraded to the current file format, that it evaluates
|
||||
(a feature may fail only by asking for a reference it lost to be picked again; what it rebuilds
|
||||
to is today's), and that damaged and hostile variants of it never panic. The `.expected.json`
|
||||
next to each is `solidcraft-cli eval` of that file by the build that saved it (bodies, volumes,
|
||||
areas, world bounding boxes), kept as a record of what that build made; no test compares with
|
||||
it.
|
||||
|
||||
| File | Saved by | Era |
|
||||
|---|---|---|
|
||||
|
||||
@@ -90,10 +90,6 @@
|
||||
],
|
||||
"timeline": [
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 1,
|
||||
"ms": 0.131943,
|
||||
@@ -104,10 +100,6 @@
|
||||
"warning": null
|
||||
},
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 2,
|
||||
"ms": 1.0812709999999999,
|
||||
@@ -118,10 +110,6 @@
|
||||
"warning": null
|
||||
},
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 3,
|
||||
"ms": 0.450002,
|
||||
@@ -132,10 +120,6 @@
|
||||
"warning": null
|
||||
},
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 4,
|
||||
"ms": 5.191514000000001,
|
||||
|
||||
@@ -54,10 +54,6 @@
|
||||
],
|
||||
"timeline": [
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 1,
|
||||
"ms": 1.178957,
|
||||
@@ -68,10 +64,6 @@
|
||||
"warning": null
|
||||
},
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 2,
|
||||
"ms": 83.24342399999999,
|
||||
@@ -82,10 +74,6 @@
|
||||
"warning": null
|
||||
},
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 3,
|
||||
"ms": 0.150889,
|
||||
@@ -96,10 +84,6 @@
|
||||
"warning": null
|
||||
},
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 4,
|
||||
"ms": 94.465481,
|
||||
@@ -110,10 +94,6 @@
|
||||
"warning": null
|
||||
},
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 5,
|
||||
"ms": 50.426736,
|
||||
@@ -124,10 +104,6 @@
|
||||
"warning": null
|
||||
},
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 6,
|
||||
"ms": 40.383005,
|
||||
@@ -138,10 +114,6 @@
|
||||
"warning": null
|
||||
},
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 7,
|
||||
"ms": 59.041461000000005,
|
||||
@@ -152,18 +124,6 @@
|
||||
"warning": null
|
||||
},
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [
|
||||
{
|
||||
"face_shift_mm": 0.0,
|
||||
"rotation_rad": 0.0,
|
||||
"strategy": "planar_fallback",
|
||||
"tolerance_mm": 0.0,
|
||||
"translation_mm": 0.0
|
||||
}
|
||||
],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 8,
|
||||
"ms": 562.5477970000001,
|
||||
@@ -171,13 +131,9 @@
|
||||
"rolled_back": false,
|
||||
"suppressed": false,
|
||||
"type": "BoxFeature",
|
||||
"warning": "Boolean retry: exact planar fallback (no displacement)"
|
||||
"warning": null
|
||||
},
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 9,
|
||||
"ms": 89.56076,
|
||||
@@ -188,10 +144,6 @@
|
||||
"warning": null
|
||||
},
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 10,
|
||||
"ms": 65.330634,
|
||||
@@ -202,10 +154,6 @@
|
||||
"warning": null
|
||||
},
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 11,
|
||||
"ms": 97.229403,
|
||||
|
||||
@@ -99,10 +99,6 @@
|
||||
],
|
||||
"timeline": [
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 1,
|
||||
"ms": 0.204585,
|
||||
@@ -113,10 +109,6 @@
|
||||
"warning": null
|
||||
},
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 2,
|
||||
"ms": 2.318662,
|
||||
@@ -127,10 +119,6 @@
|
||||
"warning": null
|
||||
},
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 3,
|
||||
"ms": 0.109196,
|
||||
@@ -141,10 +129,6 @@
|
||||
"warning": null
|
||||
},
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 4,
|
||||
"ms": 0.338859,
|
||||
@@ -155,10 +139,6 @@
|
||||
"warning": null
|
||||
},
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 5,
|
||||
"ms": 0.312259,
|
||||
@@ -169,10 +149,6 @@
|
||||
"warning": null
|
||||
},
|
||||
{
|
||||
"boolean_diagnostics": {
|
||||
"events": [],
|
||||
"truncated": false
|
||||
},
|
||||
"error": null,
|
||||
"id": 6,
|
||||
"ms": 0.23144699999999999,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Designs saved by older SolidCraft builds (tests/corpus, see its README) open and evaluate as
|
||||
//! they did when they were saved; damaged and hostile design files never panic.
|
||||
//! Designs saved by older SolidCraft builds (tests/corpus, see its README) open, are upgraded to
|
||||
//! the current format and evaluate (what they rebuild to today is today's, not what the build
|
||||
//! that saved them made); damaged and hostile design files never panic.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@@ -21,67 +22,24 @@ fn open(path: &Path) -> Session {
|
||||
s
|
||||
}
|
||||
|
||||
/// Differences that are deliberate behaviour changes made before the file format had versions
|
||||
/// (no migration can tell those files apart): (file, check, why).
|
||||
const KNOWN: [(&str, &str, &str); 2] = [
|
||||
(
|
||||
"m0-revolve-primitives",
|
||||
"Body1 bbox",
|
||||
"partial revolves turn toward the sketch normal since ac43365 (as most parametric CAD tools do); M0 turned them the other way",
|
||||
),
|
||||
("components-arm", "body_count", "Measure counted only the root component's bodies then; it measures every placed body now"),
|
||||
];
|
||||
|
||||
fn known(file: &str, check: &str) -> bool {
|
||||
KNOWN.iter().any(|(f, c, _)| *f == file && *c == check)
|
||||
}
|
||||
|
||||
/// Volumes and areas then came from coarser tessellation (and curved booleans and fillets have
|
||||
/// become exact since): the same part within this relative tolerance (the oracle's).
|
||||
const REL: f64 = 1e-3;
|
||||
|
||||
fn close(a: f64, b: f64, rel: f64) -> bool {
|
||||
(a - b).abs() <= rel * b.abs().max(1.0)
|
||||
}
|
||||
|
||||
/// Every corpus design opens, is upgraded to the current format, evaluates, and saves and reads
|
||||
/// back the same. A feature may fail only by asking for a reference it lost to be picked again
|
||||
/// (a name saved before format 14 finds nothing today).
|
||||
#[test]
|
||||
fn old_designs_open_and_evaluate_as_saved() {
|
||||
fn old_designs_open_upgrade_and_evaluate() {
|
||||
let mut problems = Vec::new();
|
||||
for p in corpus() {
|
||||
let name = p.file_stem().unwrap_or_default().to_string_lossy().to_string();
|
||||
let want: Value = serde_json::from_slice(&std::fs::read(p.with_extension("expected.json")).unwrap()).unwrap();
|
||||
let mut s = open(&p);
|
||||
for r in &s.model.results {
|
||||
if let Some(e) = &r.error {
|
||||
if let Some(e) = r.error.as_deref().filter(|e| solidcraft_engine::doc::reselect_parts(e).is_none()) {
|
||||
problems.push(format!("{name}: feature {}: {e}", r.id));
|
||||
}
|
||||
}
|
||||
assert_eq!(s.doc.format, solidcraft_engine::doc::format::format_string(solidcraft_engine::doc::format::FORMAT), "{name}: upgraded");
|
||||
let got = s.execute("inspect.measure", &json!({})).unwrap();
|
||||
if got["body_count"] != want["body_count"] && !known(&name, "body_count") {
|
||||
problems.push(format!("{name}: {} bodies, saved with {}", got["body_count"], want["body_count"]));
|
||||
}
|
||||
for w in want["bodies"].as_array().into_iter().flatten() {
|
||||
let bname = w["name"].as_str().unwrap_or_default();
|
||||
let Some(g) = got["bodies"].as_array().into_iter().flatten().find(|g| g["name"] == w["name"]) else {
|
||||
problems.push(format!("{name}: body {bname} missing"));
|
||||
continue;
|
||||
};
|
||||
for k in ["volume_mm3", "area_mm2"] {
|
||||
let (a, b) = (g[k].as_f64().unwrap_or(f64::NAN), w[k].as_f64().unwrap_or(f64::NAN));
|
||||
if !close(a, b, REL) {
|
||||
problems.push(format!("{name}: {bname} {k} {a} (saved {b})"));
|
||||
}
|
||||
}
|
||||
if known(&name, &format!("{bname} bbox")) {
|
||||
continue;
|
||||
}
|
||||
for (end, i) in [("min", 0), ("min", 1), ("min", 2), ("max", 0), ("max", 1), ("max", 2)] {
|
||||
let (a, b) = (g["bbox"][end][i].as_f64().unwrap_or(f64::NAN), w["bbox"][end][i].as_f64().unwrap_or(f64::NAN));
|
||||
if (a - b).abs() > 1e-3 * b.abs().max(1.0) {
|
||||
problems.push(format!("{name}: {bname} bbox {end}[{i}] {a} (saved {b})"));
|
||||
}
|
||||
}
|
||||
if got["body_count"].as_u64().unwrap_or(0) == 0 {
|
||||
problems.push(format!("{name}: no bodies"));
|
||||
}
|
||||
// Saved again, it reads back the same.
|
||||
let again = solidcraft_engine::doc::Document::from_json(&s.doc.to_json()).unwrap();
|
||||
@@ -94,40 +52,48 @@ fn old_designs_open_and_evaluate_as_saved() {
|
||||
}
|
||||
|
||||
/// Names saved before format 14 were worked out from geometry, pieces of a split face numbered by
|
||||
/// where their centres lay (`F2:top#2`). They keep naming the faces they named: marked as such,
|
||||
/// they are found as the build that saved them found them, without new notes.
|
||||
/// where their centres lay (`F2:top#2`). They open marked as such and find no face or edge today:
|
||||
/// each feature holding one fails, asking for its reference to be picked again, and the rest of
|
||||
/// the design builds.
|
||||
#[test]
|
||||
fn names_saved_before_exact_history_find_the_faces_they_named() {
|
||||
fn names_saved_before_exact_history_ask_for_their_faces_again() {
|
||||
let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/corpus/naming-split-pieces.solidcraft");
|
||||
let s = open(&path);
|
||||
let feature = |name: &str| s.doc.features.iter().find(|f| f.name == name).unwrap_or_else(|| panic!("{name}"));
|
||||
assert_eq!(feature("Round").edge_names, vec!["?F2:side:l2|F2:top#2".to_string()]);
|
||||
assert_eq!(feature("Bevel").edge_names, vec!["?F2:side:l4|F2:top#1".to_string()]);
|
||||
assert_eq!(feature("Raise").face_names, vec!["?F2:top#1".to_string()]);
|
||||
for r in &s.model.results {
|
||||
assert!(r.error.is_none() && r.warning.is_none(), "{}: {:?} {:?}", r.name, r.error, r.warning);
|
||||
let asked = |name: &str| {
|
||||
let r = s.model.result(feature(name).id).unwrap_or_else(|| panic!("{name}"));
|
||||
r.error.as_deref().and_then(solidcraft_engine::doc::reselect_parts).map(|(f, reference)| (f.to_string(), reference.to_string()))
|
||||
};
|
||||
assert_eq!(asked("Round"), Some(("Round".into(), "?F2:side:l2|F2:top#2".into())));
|
||||
assert_eq!(asked("Bevel"), Some(("Bevel".into(), "?F2:side:l4|F2:top#1".into())));
|
||||
assert_eq!(asked("Raise"), Some(("Raise".into(), "?F2:top#1".into())));
|
||||
for r in s.model.results.iter().filter(|r| !["Round", "Bevel", "Raise"].contains(&r.name.as_str())) {
|
||||
assert!(r.error.is_none(), "{}: {:?}", r.name, r.error);
|
||||
}
|
||||
// The raised piece is the left one, as saved (the stored point is on it).
|
||||
let state = s.model.state();
|
||||
let solid = |p: [f64; 3]| state.bodies.iter().any(|b| b.body.contains(solidcraft_engine::geom::Vec3::new(p[0], p[1], p[2])));
|
||||
assert!(solid([7.0, 15.0, 11.0]) && !solid([32.0, 15.0, 11.0]));
|
||||
assert!(!s.model.state().bodies.is_empty());
|
||||
}
|
||||
|
||||
/// Body names saved before format 14 name the pieces of a split body in the kernel's shell order.
|
||||
/// The features after the split that refer to a piece by its body name still find that piece:
|
||||
/// the far one stays `Body1` (chamfered), the near one `Body1 (1)` (filleted, coloured).
|
||||
/// Body names saved before format 14 named the pieces of a split body in the kernel's shell
|
||||
/// order: such a design opens with every feature marked `shell_piece_order` and evaluates, each
|
||||
/// body name it stores naming a piece. (Which piece takes which name is not promised to be as it
|
||||
/// was saved.) Its fillet and chamfer hold edge names saved then, which find nothing today: they
|
||||
/// ask for their edges to be picked again.
|
||||
#[test]
|
||||
fn body_names_saved_before_exact_history_keep_their_pieces() {
|
||||
fn body_names_saved_before_exact_history_open_marked_and_evaluate() {
|
||||
let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/corpus/split-body-pieces.solidcraft");
|
||||
let s = open(&path);
|
||||
assert!(s.doc.features.iter().all(|f| f.shell_piece_order), "every saved feature keeps the shell order");
|
||||
for r in &s.model.results {
|
||||
assert!(r.error.is_none() && r.warning.is_none(), "{}: {:?} {:?}", r.name, r.error, r.warning);
|
||||
let asked = r.error.as_deref().and_then(solidcraft_engine::doc::reselect_parts);
|
||||
assert!(r.error.is_none() || asked.is_some_and(|(_, reference)| reference.starts_with('?')), "{}: {:?}", r.name, r.error);
|
||||
}
|
||||
let state = s.model.state();
|
||||
let piece = |name: &str| state.bodies.iter().find(|b| b.name == name).unwrap_or_else(|| panic!("{name}"));
|
||||
let solid = |name: &str, p: [f64; 3]| piece(name).body.contains(solidcraft_engine::geom::Vec3::new(p[0], p[1], p[2]));
|
||||
assert!(solid("Body1", [20.0, 24.0, 5.0]) && solid("Body1 (1)", [20.0, 6.0, 5.0]));
|
||||
for name in ["Body1", "Body1 (1)"] {
|
||||
assert!(state.bodies.iter().any(|b| b.name == name), "{name}");
|
||||
}
|
||||
assert!(s.doc.appearances.bodies.contains_key("Body1 (1)"));
|
||||
}
|
||||
|
||||
@@ -173,6 +139,7 @@ fn flat_face_attachments() -> Vec<(String, String, usize, Option<(u32, bool)>, O
|
||||
for p in corpus() {
|
||||
let name = p.file_stem().unwrap_or_default().to_string_lossy().to_string();
|
||||
let mut s = open(&p);
|
||||
let failed_on_open: Vec<u64> = s.model.results.iter().filter(|r| r.error.is_some()).map(|r| r.id).collect();
|
||||
let state = s.model.state();
|
||||
let placed = s.placed_document();
|
||||
let mut picks = Vec::new();
|
||||
@@ -180,7 +147,7 @@ fn flat_face_attachments() -> Vec<(String, String, usize, Option<(u32, bool)>, O
|
||||
let Some(body) = state.bodies.iter().find(|b| b.definition_name() == definition && placed.model_body_component(b) == component) else {
|
||||
continue;
|
||||
};
|
||||
let mesh = body.body.tessellate(0.01).unwrap();
|
||||
let mesh = body.body.mesh(&solidcraft_kernel::MeshTolerance::chord(0.01)).unwrap();
|
||||
for index in 0..body.body.face_count() {
|
||||
if body.body.planar_face(index).is_none() {
|
||||
continue;
|
||||
@@ -224,7 +191,8 @@ fn flat_face_attachments() -> Vec<(String, String, usize, Option<(u32, bool)>, O
|
||||
let known = known_without_attachment(&name, &body, normal, distance);
|
||||
out.push((name.clone(), body, index, known, problem));
|
||||
}
|
||||
assert!(s.model.results.iter().all(|r| r.error.is_none()), "{name}: left with errors after undo");
|
||||
let failed: Vec<u64> = s.model.results.iter().filter(|r| r.error.is_some()).map(|r| r.id).collect();
|
||||
assert_eq!(failed, failed_on_open, "{name}: undo leaves the features that failed on opening, and only those, failed");
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
@@ -263,3 +263,110 @@ mod setup {
|
||||
assert!(json_of(&pkg.join("claude_desktop_config.json"))["mcpServers"]["solidcraft"].is_object());
|
||||
}
|
||||
}
|
||||
|
||||
/// Scratch files belong to the CLI process; exercise real startup, requests and shutdown.
|
||||
mod scratch_cleanup {
|
||||
use super::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
fn command(case: &str) -> (PathBuf, Command) {
|
||||
let dir = std::env::temp_dir().join(format!("solidcraft-mcp-lifecycle-{case}-{}", std::process::id()));
|
||||
std::fs::create_dir(&dir).unwrap();
|
||||
let mut command = Command::new(env!("CARGO_BIN_EXE_solidcraft-cli"));
|
||||
command
|
||||
.args(["mcp", "--headless"])
|
||||
.env("TMPDIR", &dir)
|
||||
.env("TMP", &dir)
|
||||
.env("TEMP", &dir)
|
||||
.env("SOLIDCRAFT_RECOVERY_DIR", dir.join("recovery"))
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped());
|
||||
(dir, command)
|
||||
}
|
||||
|
||||
fn scratch_folders(dir: &Path) -> Vec<PathBuf> {
|
||||
std::fs::read_dir(dir)
|
||||
.unwrap()
|
||||
.map(|entry| entry.unwrap().path())
|
||||
.filter(|path| path.file_name().unwrap().to_string_lossy().starts_with("solidcraft-mcp-"))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn initialize() -> Value {
|
||||
json!({"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2025-06-18"}})
|
||||
}
|
||||
|
||||
fn run(mut command: Command, request: Value) -> std::process::Output {
|
||||
let mut child = command.spawn().unwrap();
|
||||
writeln!(child.stdin.take().unwrap(), "{request}").unwrap();
|
||||
child.wait_with_output().unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eof_removes_only_the_process_scratch_folder() {
|
||||
let (dir, command) = command("eof");
|
||||
let unrelated = dir.join("solidcraft-mcp-unrelated");
|
||||
std::fs::create_dir(&unrelated).unwrap();
|
||||
let out = run(command, initialize());
|
||||
assert!(out.status.success(), "{}", String::from_utf8_lossy(&out.stderr));
|
||||
let reply: Value = serde_json::from_slice(&out.stdout).unwrap();
|
||||
assert!(reply.get("result").is_some(), "{reply}");
|
||||
assert_eq!(scratch_folders(&dir), vec![unrelated]);
|
||||
assert!(dir.join("recovery").is_dir(), "cleanup must leave the recovery folder alone");
|
||||
std::fs::remove_dir_all(dir).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn startup_error_removes_the_process_scratch_folder() {
|
||||
let (dir, mut command) = command("startup-error");
|
||||
let out = command.args(["--param-check", "invalid"]).stdin(Stdio::null()).output().unwrap();
|
||||
let stderr = String::from_utf8_lossy(&out.stderr);
|
||||
assert!(!out.status.success());
|
||||
assert!(stderr.contains("--param-check wants off, warn or strict"), "{stderr}");
|
||||
assert!(scratch_folders(&dir).is_empty(), "startup error left a scratch folder in {dir:?}");
|
||||
std::fs::remove_dir_all(dir).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shutdown_preserves_nonempty_scratch_folder() {
|
||||
let (dir, mut command) = command("nonempty");
|
||||
let mut child = command.spawn().unwrap();
|
||||
let mut input = child.stdin.take().unwrap();
|
||||
writeln!(input, "{}", initialize()).unwrap();
|
||||
let mut reply = String::new();
|
||||
BufReader::new(child.stdout.take().unwrap()).read_line(&mut reply).unwrap();
|
||||
let reply: Value = serde_json::from_str(&reply).unwrap();
|
||||
assert!(reply.get("result").is_some(), "{reply}");
|
||||
let scratch = scratch_folders(&dir);
|
||||
assert_eq!(scratch.len(), 1);
|
||||
let saved = scratch[0].join("keep.txt");
|
||||
std::fs::write(&saved, "saved output").unwrap();
|
||||
drop(input);
|
||||
let out = child.wait_with_output().unwrap();
|
||||
assert!(out.status.success(), "{}", String::from_utf8_lossy(&out.stderr));
|
||||
assert_eq!(std::fs::read_to_string(saved).unwrap(), "saved output");
|
||||
std::fs::remove_dir_all(dir).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_screenshot_scratch_folder_is_removed_without_a_path_scope() {
|
||||
let (dir, mut command) = command("screenshot");
|
||||
command.arg("--allow-any-path");
|
||||
// A headless server cannot capture a window. The request still allocates a scratch
|
||||
// path before the backend refuses it, without relying on a GPU or installed renderer.
|
||||
let out = run(
|
||||
command,
|
||||
json!({
|
||||
"jsonrpc": "2.0", "id": 1, "method": "tools/call",
|
||||
"params": {"name": "screenshot", "arguments": {"source": "window"}},
|
||||
}),
|
||||
);
|
||||
assert!(out.status.success(), "{}", String::from_utf8_lossy(&out.stderr));
|
||||
let reply: Value = serde_json::from_slice(&out.stdout).unwrap();
|
||||
assert_eq!(reply["result"]["isError"], true, "{reply}");
|
||||
assert!(reply["result"]["content"][0]["text"].as_str().unwrap().contains("the window gave no picture"), "{reply}");
|
||||
assert!(scratch_folders(&dir).is_empty(), "failed screenshot left a scratch folder in {dir:?}");
|
||||
std::fs::remove_dir_all(dir).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ fn model(name: &str, s: &Session) -> Value {
|
||||
.iter()
|
||||
.map(|r| json!({"id": r.id, "name": r.name, "error": r.error, "warning": r.warning, "skipped": r.skipped, "state": state(name, &r.state)}))
|
||||
.collect();
|
||||
json!({"results": results, "world": state(name, &s.world_state())})
|
||||
json!({"results": results, "world": state(name, &s.world_state().unwrap())})
|
||||
}
|
||||
|
||||
/// The same edit on both: a parameter's value when the design has one, else a new body.
|
||||
@@ -289,6 +289,60 @@ fn undoing_an_edit_after_opening_reuses_the_saved_results() {
|
||||
let _ = std::fs::remove_file(format!("{}.bak", path.display()));
|
||||
}
|
||||
|
||||
/// A saved model holds the results of the body check mode it was evaluated under: opened under
|
||||
/// the same mode it is taken as it is, under another the design is evaluated again.
|
||||
#[test]
|
||||
fn a_model_saved_under_another_check_mode_is_evaluated_again() {
|
||||
let plate = solidcraft_engine::sample::samples().into_iter().find(|s| s.name == "Sample Plate").unwrap();
|
||||
let mut built = Session::default();
|
||||
for c in (plate.script)()["commands"].as_array().unwrap() {
|
||||
built.execute(c["command"].as_str().unwrap(), &c["params"]).unwrap();
|
||||
}
|
||||
let path = temp("mode");
|
||||
built.execute("file.save_as", &json!({"path": path.to_string_lossy()})).unwrap();
|
||||
let same = open(&path);
|
||||
assert_eq!(same.model.validation(), built.model.validation());
|
||||
assert_eq!(same.model.recomputed, 0, "taken from the file");
|
||||
let mut other = Session::default();
|
||||
other.set_validation(solidcraft_engine::kernel::Validation::EachStep);
|
||||
assert_ne!(other.validation(), built.model.validation());
|
||||
other.execute("doc.open", &json!({"path": path.to_string_lossy()})).unwrap();
|
||||
assert_eq!(other.validation(), solidcraft_engine::kernel::Validation::EachStep);
|
||||
assert!(other.model.recomputed > 0, "evaluated again under its own mode");
|
||||
let _ = std::fs::remove_file(&path);
|
||||
let _ = std::fs::remove_file(format!("{}.bak", path.display()));
|
||||
}
|
||||
|
||||
/// A body made by rounds keeps, in the saved model, the rounds and the body they were made on: a
|
||||
/// later round at the corner where they meet blends them again with it whether the design was
|
||||
/// opened from its model or evaluated anew. Two edges of a 20 mm box rounded r2 (a mitre at the
|
||||
/// corner (20, 0, 20)), saved; opened, the upright edge there rounded r3: the corner all three
|
||||
/// rounded at once make (SMLib's setback corner, kernel `three_rounds_at_a_corner`), 15 faces and
|
||||
/// 7928.142563938 mm³, the same body as the design evaluated anew.
|
||||
#[test]
|
||||
fn a_later_round_after_opening_blends_the_saved_rounds_again() {
|
||||
let mut built = Session::default();
|
||||
built.execute("solid.box", &json!({"length": 20, "width": 20, "height": 20, "body_name": "Box"})).unwrap();
|
||||
built.execute("solid.fillet", &json!({"edges": [[20, 10, 20], [10, 0, 20]], "radius": 2})).unwrap();
|
||||
let path = temp("later-round");
|
||||
built.execute("file.save_as", &json!({"path": path.to_string_lossy()})).unwrap();
|
||||
let bytes = std::fs::read(&path).unwrap();
|
||||
let mut cached = open(&path);
|
||||
assert_eq!(cached.model.last_recomputed, 0, "opened without evaluating");
|
||||
let mut rebuilt = Session::new(solidcraft_engine::io::read_design(&bytes).unwrap());
|
||||
for s in [&mut cached, &mut rebuilt] {
|
||||
s.execute("solid.fillet", &json!({"edges": [[20, 0, 10]], "radius": 3})).unwrap();
|
||||
let st = s.model.state();
|
||||
let b = &st.body("Box").unwrap().body;
|
||||
let v = solidcraft_engine::kernel::measure(b).unwrap().volume;
|
||||
assert_eq!(b.face_count(), 15);
|
||||
assert!((v - 7928.142563938).abs() < 1e-6, "{v}");
|
||||
}
|
||||
assert_eq!(model("later round", &cached), model("later round", &rebuilt));
|
||||
let _ = std::fs::remove_file(&path);
|
||||
let _ = std::fs::remove_file(format!("{}.bak", path.display()));
|
||||
}
|
||||
|
||||
/// A design saved before body names were pinned (#6403) pins them as it opens, as its first
|
||||
/// command would, and opening it is no change. That command pinned them as part of itself, so
|
||||
/// undoing it went back to the unpinned design, which no evaluated result matched: the first
|
||||
|
||||
@@ -0,0 +1,403 @@
|
||||
//! Every design of the corpus loads, two builds of it name its faces and edges alike, and a
|
||||
//! name it stores that no longer finds its face or edge fails its feature. The stored names are
|
||||
//! the fillet and chamfer edges, the faces a feature picks, the face a sketch sits on and the
|
||||
//! sheet metal edges; one that finds nothing (`gone`, a sketch's face `lost`) must leave its
|
||||
//! feature failed (it asks for the reference to be picked again, never takes another face or
|
||||
//! edge), and a design that no longer loads fails the test. A build's names are each face's and
|
||||
//! edge's name with where it is (its area and centre, its length and middle); the second build
|
||||
//! must give the same, and its stored names must find the same faces and edges.
|
||||
//!
|
||||
//! Face and edge indices are left out: they are the kernel's order, which a rebuild need not
|
||||
//! keep (a fillet's or a boolean's faces come out in another order from one build to the next).
|
||||
//! How many faces a body has and what each is called is the kernel's business, not this test's.
|
||||
//!
|
||||
//! The corpus is the action matrix's (the old design files in `tests/corpus`, the examples, the
|
||||
//! demo and typical designs in `tests/action_matrix/designs`, the reference recipes), the sample
|
||||
//! designs, the engine's design fixtures and the older designs kept by the hole matrix, taper
|
||||
//! and projection tests.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use solidcraft_engine::Session;
|
||||
use solidcraft_engine::doc::{FaceAttachmentStatus, Feature, FeatureKind, ModelBody, ModelState, PlaneRef, naming};
|
||||
use solidcraft_engine::geom::Vec3;
|
||||
|
||||
fn root() -> PathBuf {
|
||||
Path::new(env!("CARGO_MANIFEST_DIR")).join("../..").canonicalize().unwrap()
|
||||
}
|
||||
|
||||
/// Where a design comes from.
|
||||
enum Source {
|
||||
File(PathBuf),
|
||||
Sample(fn() -> Value),
|
||||
}
|
||||
|
||||
/// Every design of the corpus: (`<group>/<design>`, where it comes from).
|
||||
fn designs() -> Vec<(String, Source)> {
|
||||
let root = root();
|
||||
let mut out = Vec::new();
|
||||
let mut add = |prefix: &str, dir: PathBuf, ext: &str| {
|
||||
let Ok(rd) = std::fs::read_dir(&dir) else { return };
|
||||
let mut v: Vec<PathBuf> = rd.flatten().map(|e| e.path()).filter(|p| p.extension().is_some_and(|x| x == ext)).collect();
|
||||
v.sort();
|
||||
for p in v {
|
||||
let stem = p.file_stem().unwrap_or_default().to_string_lossy().to_string();
|
||||
out.push((format!("{prefix}/{stem}"), Source::File(p)));
|
||||
}
|
||||
};
|
||||
add("corpus", root.join("apps/solidcraft-cli/tests/corpus"), "solidcraft");
|
||||
add("examples", root.join("examples"), "json");
|
||||
add("examples", root.join("examples/acceptance"), "json");
|
||||
add("regressions", root.join("examples/regressions"), "json");
|
||||
add("demo", root.join("examples/demo"), "json");
|
||||
add("typical", root.join("tests/action_matrix/designs"), "json");
|
||||
add("demo", root.join("tests/action_matrix/designs"), "solidcraft");
|
||||
add("fixtures", root.join("crates/engine/src/tests/fixtures"), "solidcraft");
|
||||
add("hole_matrix", root.join("tests/hole_matrix/old-0.9"), "json");
|
||||
add("taper", root.join("tests/taper/before-1014"), "json");
|
||||
add("projection", root.join("tests/projection/before-1508"), "json");
|
||||
let mut cases: Vec<PathBuf> =
|
||||
std::fs::read_dir(root.join("tests/oracle")).unwrap().flatten().map(|e| e.path()).filter(|p| p.join("recipe.json").is_file()).collect();
|
||||
cases.sort();
|
||||
for c in cases {
|
||||
out.push((format!("oracle/{}", c.file_name().unwrap_or_default().to_string_lossy()), Source::File(c.join("recipe.json"))));
|
||||
}
|
||||
for sample in solidcraft_engine::sample::samples() {
|
||||
let stem: String = sample.name.to_lowercase().split_whitespace().collect::<Vec<_>>().join("-");
|
||||
out.push((format!("samples/{stem}"), Source::Sample(sample.script)));
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Build a design: a design file (also a design saved as JSON) is opened, a script is run.
|
||||
fn load(source: &Source) -> Result<Session, String> {
|
||||
let mut s = Session::default();
|
||||
let script = match source {
|
||||
Source::Sample(script) => script(),
|
||||
Source::File(path) if path.extension().is_some_and(|x| x == "solidcraft") => {
|
||||
s.execute("doc.open", &json!({"path": path.to_string_lossy()})).map_err(|e| e.to_string())?;
|
||||
return Ok(s);
|
||||
}
|
||||
Source::File(path) if path.file_name().is_some_and(|n| n == "recipe.json") => {
|
||||
let out = Command::new(env!("CARGO_BIN_EXE_solidcraft-cli")).arg("recipe").arg(path).output().map_err(|e| e.to_string())?;
|
||||
if !out.status.success() {
|
||||
return Err(format!("recipe: {}", String::from_utf8_lossy(&out.stderr).trim()));
|
||||
}
|
||||
serde_json::from_slice(&out.stdout).map_err(|e| e.to_string())?
|
||||
}
|
||||
Source::File(path) => {
|
||||
let v: Value = serde_json::from_slice(&std::fs::read(path).map_err(|e| e.to_string())?).map_err(|e| e.to_string())?;
|
||||
if v.get("features").is_some() {
|
||||
s.execute("doc.open", &json!({"path": path.to_string_lossy()})).map_err(|e| e.to_string())?;
|
||||
return Ok(s);
|
||||
}
|
||||
match v.get("steps").and_then(Value::as_array) {
|
||||
// The demo's steps: an MCP `execute` each.
|
||||
Some(steps) => {
|
||||
json!({"commands": steps.iter().map(|st| json!({"command": st["command"], "params": st["params"]})).collect::<Vec<_>>()})
|
||||
}
|
||||
None => v,
|
||||
}
|
||||
}
|
||||
};
|
||||
s.run_script(&script).map_err(|e| e.to_string())?;
|
||||
if s.active_sketch.is_some() {
|
||||
let _ = s.execute("sketch.finish", &json!({}));
|
||||
}
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
/// What decides a name beyond its text: `[geometric]` when it was worked out from geometry, not
|
||||
/// exact history.
|
||||
fn flags(list: &[(bool, &str)]) -> String {
|
||||
let set: Vec<&str> = list.iter().filter(|(on, _)| *on).map(|(_, f)| *f).collect();
|
||||
if set.is_empty() { String::new() } else { format!(" [{}]", set.join(", ")) }
|
||||
}
|
||||
|
||||
/// Where each named face and edge of a body is, by name, sorted: a face's area and centre, an
|
||||
/// edge's length and middle, from the body's mesh. Two builds that name their faces alike give
|
||||
/// the same list.
|
||||
fn placed_names(b: &ModelBody) -> Vec<(String, [f64; 4])> {
|
||||
if b.body.is_mesh() {
|
||||
return Vec::new();
|
||||
}
|
||||
let m = b.mesh();
|
||||
let faces = naming::face_names(b);
|
||||
let mut sums = vec![[0.0f64; 4]; faces.len()];
|
||||
for (t, f) in m.triangles.iter().zip(&m.tri_face) {
|
||||
let [p, q, r] = t.map(|i| m.positions.get(i as usize).copied().unwrap_or(Vec3::ZERO));
|
||||
let area = (q - p).cross(r - p).len() / 2.0;
|
||||
let c = (p + q + r) / 3.0;
|
||||
if let Some(s) = sums.get_mut(*f as usize) {
|
||||
*s = [s[0] + area, s[1] + area * c.x, s[2] + area * c.y, s[3] + area * c.z];
|
||||
}
|
||||
}
|
||||
let mut out: Vec<(String, [f64; 4])> = faces
|
||||
.iter()
|
||||
.zip(&sums)
|
||||
.map(|(n, s)| {
|
||||
let a = s[0].max(1e-300);
|
||||
(format!("{} face {n}", b.name), [s[0], s[1] / a, s[2] / a, s[3] / a])
|
||||
})
|
||||
.collect();
|
||||
for (n, line) in naming::edge_names(b).iter().zip(&m.edges) {
|
||||
let length: f64 = line.windows(2).map(|w| w[1].dist(w[0])).sum();
|
||||
let mid = line.iter().fold(Vec3::ZERO, |acc, p| acc + *p) / (line.len().max(1) as f64);
|
||||
out.push((format!("{} edge {n}", b.name), [length, mid.x, mid.y, mid.z]));
|
||||
}
|
||||
out.sort_by(|x, y| x.0.cmp(&y.0).then(x.1.partial_cmp(&y.1).unwrap_or(std::cmp::Ordering::Equal)));
|
||||
out
|
||||
}
|
||||
|
||||
fn face_label(st: &ModelState, body: usize, face: usize) -> String {
|
||||
match st.bodies.get(body) {
|
||||
Some(b) => format!("{} face {}", b.name, naming::face_names(b).get(face).map(String::as_str).unwrap_or("?")),
|
||||
None => format!("body #{body} face #{face}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Every name feature `f` stores, with what it finds in the model before it, as a feature
|
||||
/// rebuilding finds it, and whether it finds anything.
|
||||
fn record_refs(out: &mut Vec<(String, bool)>, s: &Session, f: &Feature) {
|
||||
let doc = &*s.doc;
|
||||
let who = format!("ref {} ({})", f.name, f.id);
|
||||
let before = s.model.state_before(f.id);
|
||||
// Faces a feature picks by point and stores by name (shell, draft, hole, …).
|
||||
let explicit_emboss = matches!(&f.kind, FeatureKind::Emboss { face: Some(_), .. });
|
||||
if f.face_names.iter().any(|n| !n.is_empty()) {
|
||||
let fst = solidcraft_engine::doc::face_point_state(doc, &before, &f.kind, f.component).unwrap();
|
||||
for (k, (p, n)) in f.kind.face_points().into_iter().zip(&f.face_names).enumerate() {
|
||||
if n.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let (found, ok) = if explicit_emboss {
|
||||
("not checked (a cylindrical emboss finds its face on its own body)".to_string(), true)
|
||||
} else {
|
||||
match naming::locate_face_on_body(&fst, f.kind.face_body(), n, p) {
|
||||
Some(hit) => (format!("{}{}", face_label(&fst, hit.body, hit.face), flags(&[(!hit.verified, "geometric")])), true),
|
||||
None => ("gone".into(), false),
|
||||
}
|
||||
};
|
||||
out.push((format!("{who} face {k} `{n}` -> {found}"), ok));
|
||||
}
|
||||
}
|
||||
// Edges a feature picks by point and stores by name.
|
||||
if f.edge_names.iter().any(|n| !n.is_empty()) {
|
||||
let edge = |out: &mut Vec<(String, bool)>, b: &ModelBody, k: usize, n: &str, p: Vec3| {
|
||||
let (found, ok) = match naming::locate_edge(b, n, p) {
|
||||
Ok(hit) => (
|
||||
format!(
|
||||
"{} edge {}{}",
|
||||
b.name,
|
||||
naming::edge_names(b).get(hit.edge).map(String::as_str).unwrap_or("?"),
|
||||
flags(&[(!hit.verified, "geometric")])
|
||||
),
|
||||
true,
|
||||
),
|
||||
Err(e) => (format!("gone ({e})"), false),
|
||||
};
|
||||
out.push((format!("{who} edge {k} `{n}` -> {found}"), ok));
|
||||
};
|
||||
let named = |k: usize| f.edge_names.get(k).filter(|n| !n.is_empty());
|
||||
match &f.kind {
|
||||
FeatureKind::Fillet { edges, body, .. } | FeatureKind::Chamfer { edges, body, .. } => {
|
||||
let pts: Vec<Vec3> = solidcraft_engine::doc::plain(edges);
|
||||
match solidcraft_engine::doc::blend_target(&before, body, &pts).ok().and_then(|i| before.bodies.get(i)) {
|
||||
Some(b) => {
|
||||
for (k, p) in pts.iter().enumerate() {
|
||||
if let Some(n) = named(k) {
|
||||
edge(out, b, k, n, *p);
|
||||
}
|
||||
}
|
||||
}
|
||||
None => out.push((format!("{who} edges: no body to round"), false)),
|
||||
}
|
||||
}
|
||||
FeatureKind::SheetConvert { body, bends, .. } => match before.body(body) {
|
||||
Some(b) => {
|
||||
for (k, p) in solidcraft_engine::doc::plain::<Vec3, _>(bends).iter().enumerate() {
|
||||
if let Some(n) = named(k) {
|
||||
edge(out, b, k, n, *p);
|
||||
}
|
||||
}
|
||||
}
|
||||
None => out.push((format!("{who} bends: body `{body}` gone"), false)),
|
||||
},
|
||||
// Sheet metal edges are named by the sheet's panels, and found on its sheets.
|
||||
FeatureKind::SheetFlange { .. } | FeatureKind::SheetHem { .. } => {
|
||||
let pts: Vec<Vec3> = match &f.kind {
|
||||
FeatureKind::SheetFlange { edges, .. } => edges.iter().map(|e| e.point.0).collect(),
|
||||
FeatureKind::SheetHem { edges, .. } => solidcraft_engine::doc::plain(edges),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
for (k, p) in pts.iter().enumerate() {
|
||||
let Some(n) = named(k) else { continue };
|
||||
let on: Vec<&str> = before.sheets.iter().filter(|sh| sh.edge_by_name(n, *p).is_some()).map(|sh| sh.body.as_str()).collect();
|
||||
let found = if on.is_empty() { "gone".to_string() } else { format!("sheet edge on {}", on.join(", ")) };
|
||||
out.push((format!("{who} sheet edge {k} `{n}` -> {found}"), !on.is_empty()));
|
||||
}
|
||||
}
|
||||
other => {
|
||||
for (k, n) in f.edge_names.iter().enumerate().filter(|(_, n)| !n.is_empty()) {
|
||||
out.push((format!("{who} edge {k} `{n}` -> not checked ({})", other.type_name()), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// The face a sketch sits on.
|
||||
if let FeatureKind::Sketch { plane: PlaneRef::Face { name, anchor, at, .. }, .. } = &f.kind {
|
||||
let mut ok = true;
|
||||
let status = match doc.sketch_face_attachment(f.id, &s.model) {
|
||||
FaceAttachmentStatus::Healthy { target, .. } => {
|
||||
let faces = match before.body(&target.body) {
|
||||
Some(b) => match doc.anchored_face_indices(&before, b, &target) {
|
||||
Ok((indices, note)) => {
|
||||
let names = naming::face_names(b);
|
||||
let mut list: Vec<&str> = indices.iter().map(|i| names.get(*i).map(String::as_str).unwrap_or("?")).collect();
|
||||
list.sort();
|
||||
format!("{} faces {}{}", b.name, list.join(", "), flags(&[(note.is_some(), "changed cap")]))
|
||||
}
|
||||
Err(e) => {
|
||||
ok = false;
|
||||
format!("no faces ({e})")
|
||||
}
|
||||
},
|
||||
None => {
|
||||
ok = false;
|
||||
format!("body `{}` gone", target.body)
|
||||
}
|
||||
};
|
||||
format!("healthy: {faces}")
|
||||
}
|
||||
FaceAttachmentStatus::Lost { message, .. } => {
|
||||
ok = false;
|
||||
format!("lost: {message}")
|
||||
}
|
||||
FaceAttachmentStatus::Legacy { .. } => match name {
|
||||
// The face its name finds, as the sketch finds it.
|
||||
Some(n) => {
|
||||
let fst = solidcraft_engine::doc::state_in_frame(doc, &before, f.component).unwrap();
|
||||
match naming::locate_face(&fst, n, *at) {
|
||||
Some(hit) => format!("by name: {}{}", face_label(&fst, hit.body, hit.face), flags(&[(!hit.verified, "geometric")])),
|
||||
None => {
|
||||
ok = false;
|
||||
"by name: gone".into()
|
||||
}
|
||||
}
|
||||
}
|
||||
None => "by its pick".into(),
|
||||
},
|
||||
FaceAttachmentStatus::Sheet { .. } => "sheet face".into(),
|
||||
FaceAttachmentStatus::Fixed { .. } => "fixed".into(),
|
||||
};
|
||||
let stored = match (anchor, name) {
|
||||
(Some(a), _) => {
|
||||
let kind =
|
||||
a.target.certificate.as_ref().and_then(|c| serde_json::to_value(c).ok()).and_then(|v| v["kind"].as_str().map(String::from));
|
||||
format!("`{}` on {} ({})", a.target.name, a.target.body, kind.unwrap_or_else(|| "no certificate".into()))
|
||||
}
|
||||
(None, Some(n)) => format!("`{n}`"),
|
||||
(None, None) => "by its pick".into(),
|
||||
};
|
||||
out.push((format!("{who} plane {stored} -> {status}"), ok));
|
||||
}
|
||||
}
|
||||
|
||||
/// A build of a design: where its faces and edges are by name, and what every name it stores
|
||||
/// finds.
|
||||
struct Build {
|
||||
names: Vec<(String, [f64; 4])>,
|
||||
refs: Vec<(String, bool)>,
|
||||
}
|
||||
|
||||
fn build(s: &Session) -> Build {
|
||||
let names = s.model.state().bodies.iter().flat_map(placed_names).collect();
|
||||
let mut refs = Vec::new();
|
||||
for f in s.doc.features.iter().filter(|f| !f.suppressed) {
|
||||
let from = refs.len();
|
||||
record_refs(&mut refs, s, f);
|
||||
// A name that finds nothing is sound when its feature fails: it asks for the reference
|
||||
// to be picked again.
|
||||
if s.model.result(f.id).is_some_and(|r| r.error.is_some()) {
|
||||
refs.iter_mut().skip(from).for_each(|(_, ok)| *ok = true);
|
||||
}
|
||||
}
|
||||
Build { names, refs }
|
||||
}
|
||||
|
||||
/// Where two builds name their faces and edges apart (at most `cap` of them): a name only one
|
||||
/// has, or a face or edge of that name somewhere else (1e-6 of its size, to the mesh's rounding).
|
||||
fn differences(a: &Build, b: &Build, cap: usize) -> Vec<String> {
|
||||
let mut out = Vec::new();
|
||||
if a.names.len() != b.names.len() {
|
||||
out.push(format!("{} named faces and edges, then {}", a.names.len(), b.names.len()));
|
||||
}
|
||||
for ((n, x), (m, y)) in a.names.iter().zip(&b.names) {
|
||||
let scale = x.iter().chain(y).fold(1.0f64, |s, v| s.max(v.abs()));
|
||||
if n != m || x.iter().zip(y).any(|(p, q)| (p - q).abs() > 1e-6 * scale) {
|
||||
out.push(format!("{n} at {x:?}, then {m} at {y:?}"));
|
||||
}
|
||||
}
|
||||
for ((r, _), (q, _)) in a.refs.iter().zip(&b.refs) {
|
||||
if r != q {
|
||||
out.push(format!("{r}, then {q}"));
|
||||
}
|
||||
}
|
||||
if a.refs.len() != b.refs.len() {
|
||||
out.push(format!("{} stored names, then {}", a.refs.len(), b.refs.len()));
|
||||
}
|
||||
out.truncate(cap);
|
||||
out
|
||||
}
|
||||
|
||||
/// Designs whose names differ from one build to the next today: (design, why). Their
|
||||
/// differences are printed, not failed on, until the cause is fixed; a stored name that finds
|
||||
/// nothing still fails.
|
||||
const UNSTABLE: [(&str, &str); 1] = [(
|
||||
"regressions/closed_sweep_torus_cut",
|
||||
"the halves of each swept wall lie level along every axis, so which is `#1` and which `#2` follows the order the kernel gives the faces in, which changes from one build to the next",
|
||||
)];
|
||||
|
||||
#[test]
|
||||
fn corpus_designs_name_alike_every_build_and_fail_where_a_reference_is_gone() {
|
||||
let designs = designs();
|
||||
let next = std::sync::atomic::AtomicUsize::new(0);
|
||||
let results = std::sync::Mutex::new(Vec::new());
|
||||
let workers = std::thread::available_parallelism().map_or(4, |n| n.get()).min(designs.len().max(1));
|
||||
std::thread::scope(|scope| {
|
||||
for _ in 0..workers {
|
||||
scope.spawn(|| {
|
||||
while let Some((name, source)) = designs.get(next.fetch_add(1, std::sync::atomic::Ordering::Relaxed)) {
|
||||
let got = load(source).and_then(|first| Ok((build(&first), build(&load(source)?))));
|
||||
results.lock().unwrap_or_else(|e| e.into_inner()).push((name.clone(), got));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
let mut results = results.into_inner().unwrap_or_else(|e| e.into_inner());
|
||||
results.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
let mut problems = Vec::new();
|
||||
for (name, got) in &results {
|
||||
let (first, second) = match got {
|
||||
Ok(builds) => builds,
|
||||
Err(e) => {
|
||||
problems.push(format!("{name}: does not load: {e}"));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let lost: Vec<&str> = first.refs.iter().filter(|(_, ok)| !ok).map(|(r, _)| r.as_str()).collect();
|
||||
if !lost.is_empty() {
|
||||
problems.push(format!("{name}: stored names that find nothing, in features that build:\n {}", lost.join("\n ")));
|
||||
}
|
||||
let apart = differences(first, second, 40);
|
||||
if !apart.is_empty() {
|
||||
match UNSTABLE.iter().find(|(design, _)| design == name) {
|
||||
Some((_, why)) => eprintln!("{name} (not checked: {why}):\n {}", apart.join("\n ")),
|
||||
None => problems.push(format!("{name}: two builds name it apart (first, then second):\n {}", apart.join("\n "))),
|
||||
}
|
||||
}
|
||||
}
|
||||
assert!(problems.is_empty(), "{} of {} design(s) of the corpus:\n{}", problems.len(), results.len(), problems.join("\n"));
|
||||
}
|
||||
@@ -31,7 +31,7 @@ struct Topology {
|
||||
|
||||
/// Every solid body's topology, by name.
|
||||
fn topology(s: &Session) -> BTreeMap<String, Topology> {
|
||||
let st = s.world_state();
|
||||
let st = s.world_state().unwrap();
|
||||
st.bodies
|
||||
.iter()
|
||||
.filter(|b| !b.body.is_mesh())
|
||||
@@ -142,16 +142,14 @@ fn a_parameter_edit_rebuilds_like_a_fresh_build_and_reverts_exactly() {
|
||||
assert!(problems.is_empty(), "{}", problems.join("\n"));
|
||||
}
|
||||
|
||||
/// The README demo's Raspberry Pi case (`examples/demo/enclosure_ui_mcp.py --story pi`): its
|
||||
/// face count after each feature, and each outer side (the four walls and the bottom) one face
|
||||
/// throughout: (a) built at height 24, (b) built at 24 and changed to 32 in Parameters, (c)
|
||||
/// built at 32. The port cut-outs and the floor vents used to split the long wall (or the end
|
||||
/// wall at 24) into strips along the openings' tops and bottoms.
|
||||
/// The README demo's Raspberry Pi case (`examples/demo/enclosure_ui_mcp.py --story pi`): after
|
||||
/// each feature, each outer side (the four walls and the bottom) is one face and the case is as
|
||||
/// tall as its height, and a height changed in Parameters rebuilds the same topology as one
|
||||
/// built at that height: (a) built at height 24, (b) built at 24 and changed to 32, (c) built at
|
||||
/// 32. The port cut-outs and the floor vents used to split the long wall (or the end wall at 24)
|
||||
/// into strips along the openings' tops and bottoms.
|
||||
#[test]
|
||||
fn pi_case_faces_per_step_at_both_heights() {
|
||||
// Sketch1 (1) .. RectangularPattern2 (14): the faces once features 1..=k are built.
|
||||
const FACES: [(usize, usize); 13] =
|
||||
[(2, 6), (3, 10), (4, 19), (5, 19), (6, 25), (7, 43), (8, 43), (9, 55), (10, 55), (11, 59), (12, 59), (13, 63), (14, 79)];
|
||||
fn pi_case_sides_stay_whole_at_both_heights() {
|
||||
let dir = scratch_dir("pi");
|
||||
let design: Value = serde_json::from_slice(&std::fs::read(root().join("tests/action_matrix/designs/pi-case.sc")).unwrap()).unwrap();
|
||||
let at = |height: &str, file: &str| {
|
||||
@@ -174,12 +172,13 @@ fn pi_case_faces_per_step_at_both_heights() {
|
||||
for (case, s, h) in [("(a) at 24", a, 24.), ("(b) 24 changed to 32", b, 32.), ("(c) at 32", c, 32.)] {
|
||||
assert!(errors(&s).is_empty(), "{case}: features fail: {:?}", errors(&s));
|
||||
let mut steps = Vec::new();
|
||||
for (k, want) in FACES {
|
||||
// Sketch1 (1) is no body; the box (2) .. RectangularPattern2 (14).
|
||||
for k in 2..=14 {
|
||||
let mut t = s.scratch();
|
||||
t.execute("timeline.rollback", &json!({"position": k})).unwrap();
|
||||
let st = t.world_state();
|
||||
let st = t.world_state().unwrap();
|
||||
let body = st.bodies.iter().find(|b| b.name == "Body1").unwrap();
|
||||
assert_eq!(body.body.face_count(), want, "{case}: faces after feature {k}");
|
||||
assert!(body.body.validity().is_empty(), "{case}: after feature {k}: {:?}", body.body.validity());
|
||||
// Each outer side: the faces in its plane.
|
||||
for (n, d) in [([0., -1., 0.], 33.), ([0., 1., 0.], 33.), ([-1., 0., 0.], 47.5), ([1., 0., 0.], 47.5), ([0., 0., -1.], 0.)] {
|
||||
let on = (0..body.body.face_count())
|
||||
|
||||
@@ -65,7 +65,10 @@ fn allow_errors_writes_anyway_and_a_saved_design_keeps_failing() {
|
||||
let output = cli().arg("run").arg(dir.broken_script()).arg("--allow-errors").arg("--out").arg(&step).arg("--save").arg(&saved).output().unwrap();
|
||||
assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr));
|
||||
let measured: Value = serde_json::from_slice(&output.stdout).unwrap();
|
||||
assert_eq!(measured["total"]["volume_mm3"], json!(4000.0), "the box without its fillet");
|
||||
// SMLib integrates the volume (a relative accuracy of 1e-4 asked for; a box comes out within
|
||||
// a few units in the last place), so the box's 4000 mm³ is checked to 1e-9, not to the bit.
|
||||
let volume = measured["total"]["volume_mm3"].as_f64().unwrap();
|
||||
assert!((volume - 4000.0).abs() < 4000.0 * 1e-9, "the box without its fillet: {volume}");
|
||||
assert!(std::fs::metadata(&step).unwrap().len() > 0);
|
||||
// The saved design still holds the failing feature: running it fails too, `eval` reports it.
|
||||
let again = dir.0.join("again.step");
|
||||
|
||||
@@ -171,9 +171,9 @@ fn a_saved_view_is_its_camera_and_a_standard_view_is_unchanged() {
|
||||
let top = dir.snapshot(&script, "top.png", &["--view", "top"]);
|
||||
let mut s = Session::default();
|
||||
s.run_script(&serde_json::from_slice(&std::fs::read(&script).unwrap()).unwrap()).unwrap();
|
||||
let mut cam = solidcraft_engine::view::home_camera(&s);
|
||||
let mut cam = solidcraft_engine::view::home_camera(&s).unwrap();
|
||||
cam.set_view(solidcraft_engine::render::StandardView::Top);
|
||||
let scene = solidcraft_engine::view::scene(&s, &cam);
|
||||
let scene = solidcraft_engine::view::scene(&s, &cam).unwrap();
|
||||
assert_same(&top, &solidcraft_engine::capture::png(&scene, &cam, SIZE.0, SIZE.1).unwrap().bytes, "the home camera turned to the top");
|
||||
assert_same(&top, &dir.mcp(&script, "mcp-top.png", json!({"view": "top"})), "the MCP top view");
|
||||
assert_differs(&top, &saved, "top and the saved view");
|
||||
@@ -335,9 +335,9 @@ fn a_forced_cpu_snapshot_is_the_cpu_rasterizers_and_says_so() {
|
||||
assert!(stderr.contains("CPU rasterizer"), "{stderr}");
|
||||
let mut s = Session::default();
|
||||
s.run_script(&serde_json::from_slice(&std::fs::read(&script).unwrap()).unwrap()).unwrap();
|
||||
let mut cam = solidcraft_engine::view::home_camera(&s);
|
||||
let mut cam = solidcraft_engine::view::home_camera(&s).unwrap();
|
||||
cam.set_view(solidcraft_engine::render::StandardView::Top);
|
||||
let scene = solidcraft_engine::view::scene(&s, &cam);
|
||||
let scene = solidcraft_engine::view::scene(&s, &cam).unwrap();
|
||||
let cpu = solidcraft_engine::render::render_png(&scene, &cam, SIZE.0 as usize, SIZE.1 as usize).unwrap();
|
||||
assert_eq!(std::fs::read(dir.0.join("cpu.png")).unwrap(), cpu);
|
||||
}
|
||||
|
||||
@@ -48,11 +48,11 @@ fn key(v: Vec3) -> [i64; 3] {
|
||||
fn straight_edges_are_one_edge_after_many_cuts() {
|
||||
for (what, s) in [("as saved", open(&design())), ("built from scratch", rebuilt())] {
|
||||
assert_eq!(errors(&s), vec![], "{what}: every feature evaluates");
|
||||
let st = s.world_state();
|
||||
let st = s.world_state().unwrap();
|
||||
let bodies: Vec<_> = st.bodies.iter().filter(|b| !b.body.is_mesh()).collect();
|
||||
assert_eq!(bodies.len(), 1, "{what}: one body");
|
||||
let body = &bodies[0].body;
|
||||
let mesh = body.tessellate(0.01).unwrap();
|
||||
let mesh = body.mesh(&solidcraft_kernel::MeshTolerance::chord(0.01)).unwrap();
|
||||
// Every edge is drawn as one polyline; a straight one as one segment.
|
||||
let mut at: HashMap<[i64; 3], Vec<usize>> = HashMap::new();
|
||||
for (i, e) in mesh.edges.iter().enumerate() {
|
||||
|
||||
@@ -14,8 +14,11 @@ name = "solidcraft-web"
|
||||
path = "src/main.rs"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
default = ["profile"]
|
||||
bundled-render-library = ["solidcraft-engine/bundled-render-library"]
|
||||
# The profiler, compiled in so any build can say where its time goes (the Profile operations
|
||||
# preference, SOLIDCRAFT_PROFILE=1, diagnostics.profile); it records nothing until asked.
|
||||
profile = ["solidcraft-engine/profile"]
|
||||
|
||||
[dependencies]
|
||||
solidcraft-engine = { workspace = true }
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
//! Links the browser build with an 8 MiB stack: solidcore's kernel algorithms (subdivision,
|
||||
//! intersection, filleting) recurse legitimately, and wasm has no guard page to catch an
|
||||
//! overflow (solidcore's README recommends 8 MiB).
|
||||
|
||||
fn main() {
|
||||
if std::env::var("CARGO_CFG_TARGET_ARCH").is_ok_and(|a| a == "wasm32") {
|
||||
println!("cargo:rustc-link-arg=-zstack-size=8388608");
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,12 @@ publish = false
|
||||
default-run = "solidcraft"
|
||||
|
||||
[features]
|
||||
default = ["raytrace-cpu", "bundled-render-library"]
|
||||
default = ["raytrace-cpu", "bundled-render-library", "profile"]
|
||||
bundled-render-library = ["solidcraft-engine/bundled-render-library"]
|
||||
raytrace-cpu = ["solidcraft-engine/raytrace-cpu"]
|
||||
# The profiler, compiled in so any build can say where its time goes (the Profile operations
|
||||
# preference, SOLIDCRAFT_PROFILE=1, diagnostics.profile); it records nothing until asked.
|
||||
profile = ["solidcraft-engine/profile"]
|
||||
|
||||
# The workspace lints, except unsafe_code: denied, not forbidden, so that src/open_documents.rs
|
||||
# (macOS file-open events), src/native_crash.rs (Windows exception filter), src/native_crash_macos.rs
|
||||
|
||||
@@ -22,6 +22,10 @@ const MAX_BAD_LINES: usize = 8;
|
||||
/// How long a subscribed connection waits after a `design.changed` line before the next: what
|
||||
/// changes meanwhile (a parameter dragged) goes out as one line, the latest (#6985).
|
||||
const EVENT_GAP: Duration = Duration::from_millis(100);
|
||||
/// How long a new connection has to show the token. A client connects when it has a request to
|
||||
/// send, so this is plenty; one that doesn't send it loses the connection, and its seat among
|
||||
/// [`MAX_CONNECTIONS`] goes to someone else.
|
||||
const TOKEN_WAIT: Duration = Duration::from_secs(5);
|
||||
|
||||
static OPEN: AtomicUsize = AtomicUsize::new(0);
|
||||
/// The token files this launch wrote (its control port, the agent chat's endpoint) and the
|
||||
@@ -225,23 +229,53 @@ fn unauthorized() -> Value {
|
||||
/// Refuse the connection: the reply, then the end of what we send, then what the client had
|
||||
/// already sent read and dropped (a little, briefly). Closing a socket with unread input resets
|
||||
/// the connection, and a reset can reach the client before it has read the refusal.
|
||||
fn refuse(out: &mut TcpStream, reader: &mut BufReader<TcpStream>) {
|
||||
fn refuse(out: &mut TcpStream, reader: &mut BufReader<ClientInput>) {
|
||||
use std::io::Read;
|
||||
let _ = send(out, &unauthorized());
|
||||
let _ = out.shutdown(std::net::Shutdown::Write);
|
||||
let _ = reader.get_ref().set_read_timeout(Some(Duration::from_millis(500)));
|
||||
reader.get_mut().deadline = Some(Instant::now() + Duration::from_millis(500));
|
||||
let _ = std::io::copy(&mut reader.by_ref().take(64 * 1024), &mut std::io::sink());
|
||||
}
|
||||
|
||||
/// What a client sends. Until it shows the token, all of its reads share one deadline, so neither
|
||||
/// blank lines nor a line sent a byte at a time buy it more time. After that it may stay idle as
|
||||
/// long as it likes.
|
||||
struct ClientInput {
|
||||
socket: TcpStream,
|
||||
deadline: Option<Instant>,
|
||||
}
|
||||
|
||||
impl ClientInput {
|
||||
/// No deadline any more: the client showed the token.
|
||||
fn admit(&mut self) -> std::io::Result<()> {
|
||||
self.deadline = None;
|
||||
self.socket.set_read_timeout(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::io::Read for ClientInput {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
if let Some(deadline) = self.deadline {
|
||||
let left = deadline.saturating_duration_since(Instant::now());
|
||||
if left.is_zero() {
|
||||
return Err(std::io::ErrorKind::TimedOut.into());
|
||||
}
|
||||
self.socket.set_read_timeout(Some(left))?;
|
||||
}
|
||||
self.socket.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// How long a request waits for the app's reply: most requests, and a batch (or every request,
|
||||
/// on a patient connection).
|
||||
/// on a patient connection); and how long a new connection has to show the token.
|
||||
#[derive(Clone, Copy)]
|
||||
struct Waits {
|
||||
short: Duration,
|
||||
long: Duration,
|
||||
token: Duration,
|
||||
}
|
||||
|
||||
const WAITS: Waits = Waits { short: Duration::from_secs(60), long: Duration::from_secs(3600) };
|
||||
const WAITS: Waits = Waits { short: Duration::from_secs(60), long: Duration::from_secs(3600), token: TOKEN_WAIT };
|
||||
|
||||
/// A request that timed out but is still running in the app (#1618): its id and method, and where
|
||||
/// its reply will land.
|
||||
@@ -543,7 +577,7 @@ fn serve_with(
|
||||
let connection = running.connections.fetch_add(1, Ordering::Relaxed) + 1;
|
||||
let Ok(read) = stream.try_clone() else { return };
|
||||
let mut out = stream;
|
||||
let mut reader = BufReader::new(read);
|
||||
let mut reader = BufReader::new(ClientInput { socket: read, deadline: Some(Instant::now() + waits.token) });
|
||||
let mut bad = 0usize;
|
||||
let mut authenticated = false;
|
||||
let mut pending: Option<Pending> = None;
|
||||
@@ -554,6 +588,10 @@ fn serve_with(
|
||||
let line = match read_line(&mut reader, MAX_LINE) {
|
||||
Ok(Some(l)) => String::from_utf8_lossy(&l).into_owned(),
|
||||
Ok(None) => break,
|
||||
// Closed without a word: a client that connected early and only now sends its first
|
||||
// request would read a line here as the reply to it. It finds the connection closed
|
||||
// and opens another.
|
||||
Err(e) if !authenticated && matches!(e.kind(), std::io::ErrorKind::TimedOut | std::io::ErrorKind::WouldBlock) => break,
|
||||
Err(e) => {
|
||||
let _ = answer(&mut out, events.as_ref(), &json!({"ok": false, "error": e.to_string()}));
|
||||
break;
|
||||
@@ -565,7 +603,12 @@ fn serve_with(
|
||||
// The first request must carry the token; later ones on the same connection may.
|
||||
if !authenticated {
|
||||
match token_of(&line) {
|
||||
Some(t) if control_auth::tokens_match(&t, token) => authenticated = true,
|
||||
Some(t) if control_auth::tokens_match(&t, token) => {
|
||||
if reader.get_mut().admit().is_err() {
|
||||
break;
|
||||
}
|
||||
authenticated = true;
|
||||
}
|
||||
_ => {
|
||||
refuse(&mut out, &mut reader);
|
||||
break;
|
||||
@@ -675,6 +718,80 @@ fn serve_with(
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// A connection that doesn't show the token in time is closed without a word, however it fills
|
||||
/// the time. One that does keeps working when it idles for longer than that.
|
||||
#[test]
|
||||
fn the_token_has_a_time_limit_and_an_authenticated_client_may_idle() {
|
||||
use std::io::Read;
|
||||
let token = "0123456789abcdef0123456789abcdef";
|
||||
let limit = Duration::from_millis(500);
|
||||
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||
let addr = listener.local_addr().unwrap();
|
||||
let (tx, rx) = channel::<ControlRequest>();
|
||||
std::thread::spawn(move || {
|
||||
for req in rx {
|
||||
let _ = req.reply.send(json!({"ok": true, "result": req.method}));
|
||||
}
|
||||
});
|
||||
static RUNNING: Running = Running::new();
|
||||
let waits = Waits { short: Duration::from_secs(10), long: Duration::from_secs(10), token: limit };
|
||||
std::thread::spawn(move || {
|
||||
for stream in listener.incoming().flatten() {
|
||||
let tx = tx.clone();
|
||||
std::thread::spawn(move || serve_with(stream, token, tx, egui::Context::default(), false, waits, &RUNNING, &DESIGN_WATCH));
|
||||
}
|
||||
});
|
||||
let open = || {
|
||||
let s = TcpStream::connect(addr).unwrap();
|
||||
s.set_read_timeout(Some(Duration::from_secs(20))).unwrap();
|
||||
s
|
||||
};
|
||||
// Sends `drip` every 50 ms until the server closes; what it read, and when it closed.
|
||||
let closed_after = |drip: &[u8]| {
|
||||
let mut s = open();
|
||||
let start = Instant::now();
|
||||
s.set_read_timeout(Some(Duration::from_millis(50))).unwrap();
|
||||
let mut got = Vec::new();
|
||||
loop {
|
||||
let mut buf = [0u8; 256];
|
||||
match s.read(&mut buf) {
|
||||
Ok(0) => break,
|
||||
Ok(n) => got.extend_from_slice(buf.get(..n).unwrap()),
|
||||
Err(e) if matches!(e.kind(), std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut) => {
|
||||
assert!(start.elapsed() < Duration::from_secs(20), "still open");
|
||||
let _ = s.write_all(drip);
|
||||
}
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
(got, start.elapsed())
|
||||
};
|
||||
for drip in [&b""[..], b"\n", b"\r\n \n", b" ", b"{"] {
|
||||
let (got, took) = closed_after(drip);
|
||||
assert!(got.is_empty(), "{drip:?} was answered: {}", String::from_utf8_lossy(&got));
|
||||
assert!(took >= limit.saturating_sub(Duration::from_millis(50)), "{drip:?} closed after {took:?}");
|
||||
}
|
||||
|
||||
let mut s = open();
|
||||
let mut lines = BufReader::new(s.try_clone().unwrap());
|
||||
let mut ask = |line: Value| -> Value {
|
||||
writeln!(s, "{line}").unwrap();
|
||||
let mut l = String::new();
|
||||
lines.read_line(&mut l).unwrap();
|
||||
serde_json::from_str(&l).unwrap()
|
||||
};
|
||||
assert_eq!(ask(json!({"id": 1, "method": "ui.view", "token": token}))["ok"], true);
|
||||
std::thread::sleep(limit * 3);
|
||||
assert_eq!(ask(json!({"id": 2, "method": "ui.view"}))["ok"], true, "idle for longer than the limit");
|
||||
|
||||
// A wrong token is still answered, at once.
|
||||
let mut wrong = open();
|
||||
writeln!(wrong, "{}", json!({"id": 1, "method": "ui.view", "token": "nope"})).unwrap();
|
||||
let mut l = String::new();
|
||||
BufReader::new(wrong).read_line(&mut l).unwrap();
|
||||
assert!(l.contains("unauthorized"), "{l}");
|
||||
}
|
||||
|
||||
/// #1618: a request that outlasts its wait says it is still running; the connection keeps its
|
||||
/// reply for `control.pending`, and refuses other requests as `busy` until it lands, so none
|
||||
/// is queued to run unseen behind it.
|
||||
@@ -698,7 +815,7 @@ mod tests {
|
||||
let _ = req.reply.send(json!({"ok": true, "result": req.method}));
|
||||
}
|
||||
});
|
||||
let waits = Waits { short: Duration::from_millis(100), long: Duration::from_millis(100) };
|
||||
let waits = Waits { short: Duration::from_millis(100), long: Duration::from_millis(100), token: TOKEN_WAIT };
|
||||
static RUNNING: Running = Running::new();
|
||||
std::thread::spawn(move || {
|
||||
if let Some(stream) = listener.incoming().flatten().next() {
|
||||
@@ -764,7 +881,7 @@ mod tests {
|
||||
let _ = req.reply.send(json!({"ok": true, "result": req.method}));
|
||||
}
|
||||
});
|
||||
let waits = Waits { short: Duration::from_millis(100), long: Duration::from_secs(60) };
|
||||
let waits = Waits { short: Duration::from_millis(100), long: Duration::from_secs(60), token: TOKEN_WAIT };
|
||||
std::thread::spawn(move || {
|
||||
for stream in listener.incoming().flatten() {
|
||||
let tx = tx.clone();
|
||||
|
||||
@@ -895,6 +895,8 @@ fn main() -> eframe::Result {
|
||||
if let Err(e) = startup::relaunch_with_version_compat() {
|
||||
log::warn!("startup: OCLP check failed (continuing normally): {e}");
|
||||
}
|
||||
// SOLIDCRAFT_PROFILE=1: the profiler records from the start (diagnostics.profile reads it).
|
||||
solidcraft_engine::timing::profile_from_env();
|
||||
// Validate before legacy adoption, crash markers or any user preference read.
|
||||
let settings = settings_dir().map_err(|error| eframe::Error::AppCreation(error.into()))?;
|
||||
if std::env::var_os("SOLIDCRAFT_SETTINGS_DIR").is_none() {
|
||||
|
||||
@@ -237,6 +237,7 @@ settings_monitor false
|
||||
settings_mouse false
|
||||
settings_palette false
|
||||
settings_plug false
|
||||
settings_profile false
|
||||
sheet false
|
||||
sheet_rules false
|
||||
shell false
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<title>Settings profile</title>
|
||||
<circle cx="32" cy="19" r="8" fill="none" stroke="currentColor" stroke-width="5.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 53C12 44.7157 20.9543 38 32 38C43.0457 38 52 44.7157 52 53" fill="none" stroke="currentColor" stroke-width="5.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 413 B |
+193
-19
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "مستطيلي",
|
||||
"Rectangular Tube": "أنبوب مستطيل",
|
||||
"Reference face": "الوجه المرجعي",
|
||||
"Reference plane": "المستوى المرجعي",
|
||||
"Relief Depth": "عمق التفريغ",
|
||||
"Relief Width": "عرض التفريغ",
|
||||
"Remove Tools": "إزالة الأدوات",
|
||||
@@ -494,7 +495,8 @@
|
||||
"Rename Folder": "إعادة تسمية المجلد",
|
||||
"Render Stage": "مرحلة التصيير",
|
||||
"Reopen designs": "إعادة فتح التصاميم",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "إعادة فتح التصاميم التي كانت مفتوحة عند آخر إغلاق لـ SolidCraft",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "إعادة فتح التصاميم التي كانت مفتوحة آخر مرة",
|
||||
"Repair Part": "إصلاح الجزء",
|
||||
"Replace existing library file": "استبدال ملف المكتبة الموجود",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "الإبلاغ عن خطأ — راجع لقطة الشاشة وافتح مسودة GitHub",
|
||||
"Report copied. Keep this text to submit on GitHub when you are online.": "تم نسخ التقرير. احتفظ بهذا النص لإرساله على GitHub عند الاتصال بالإنترنت.",
|
||||
@@ -789,6 +791,7 @@
|
||||
"browser.edit_canvas_title": "تعديل اللوحة: {name}",
|
||||
"browser.edit_point_cloud_title": "تحرير سحابة النقاط: {name}",
|
||||
"browser.move_title": "تحريك: {name}",
|
||||
"browser.needs_repair": "{name}: يحتاج إلى إصلاح",
|
||||
"browser.parameters": "المعاملات ({count})",
|
||||
"browser.precision": "الدقة: {precision}",
|
||||
"browser.sketch_attachment_lost": "{name} (فُقد الارتباط)",
|
||||
@@ -833,6 +836,7 @@
|
||||
"cmd.assembly_mass_properties": "خصائص الكتلة",
|
||||
"cmd.body.print_face": "وجه الطباعة",
|
||||
"cmd.body_rename": "إعادة تسمية الجسم",
|
||||
"cmd.body_repair": "إصلاح الجزء",
|
||||
"cmd.browser_collapse_all": "طي الكل",
|
||||
"cmd.browser_expand_all": "توسيع الكل",
|
||||
"cmd.browser_group": "مجموعة جديدة",
|
||||
@@ -917,6 +921,8 @@
|
||||
"cmd.diagnostics_crash_reports": "تقارير الأعطال",
|
||||
"cmd.diagnostics_crash_reports_clear": "حذف تقارير الأعطال",
|
||||
"cmd.diagnostics_crash_test": "اختبار العطل",
|
||||
"cmd.diagnostics_profile": "التحليل الزمني",
|
||||
"cmd.diagnostics_validation": "التحقق",
|
||||
"cmd.doc_recover": "استعادة التصميم",
|
||||
"cmd.doc_recovery_discard": "تجاهل التصميم المستعاد",
|
||||
"cmd.doc_recovery_list": "التصاميم القابلة للاستعادة",
|
||||
@@ -954,6 +960,7 @@
|
||||
"cmd.frame_profile_list": "مكتبة المقاطع",
|
||||
"cmd.frame_profile_properties": "خصائص المقطع",
|
||||
"cmd.inspect_accessibility": "تحليل إمكانية الوصول",
|
||||
"cmd.inspect_analysis": "تحليل العرض",
|
||||
"cmd.inspect_center_of_mass": "مركز الكتلة",
|
||||
"cmd.inspect_clearance": "الخلوص",
|
||||
"cmd.inspect_colored_components": "تفعيل ألوان المكوّنات",
|
||||
@@ -977,6 +984,7 @@
|
||||
"cmd.joint_delete": "حذف المفصل",
|
||||
"cmd.joint_drive": "تحريك المفاصل",
|
||||
"cmd.joint_edit": "تعديل المفصل",
|
||||
"cmd.joint_find_limits": "إيجاد حدود المفصل",
|
||||
"cmd.joint_gear": "ترس",
|
||||
"cmd.joint_inspect": "فحص المفصل",
|
||||
"cmd.joint_limits": "حدود المفصل",
|
||||
@@ -1358,6 +1366,7 @@
|
||||
"crash.unclean_explanation": "اكتُشف عند تشغيل لاحق، ولم يُسجَّل وقت الخروج بدقة. قد يسبب إيقاف تشغيل النظام أو الإيقاف القسري خروجًا غير سليم أيضًا.",
|
||||
"delete.and_more": "… و{count} غيرها",
|
||||
"diagnostic.empty_boolean_component": "لا يوجد جسم للقص أو التقاطع في المكوّن '{component}'؛ قد تتداخل الأداة مع '{body}' في المكوّن '{other}' — نشّط ذلك المكوّن أو اختر الجسم هدفًا",
|
||||
"diagnostic.reselect_reference": "{feature}: المرجع `{reference}` لم يعد موجودًا كما اختير؛ أعد اختياره",
|
||||
"dialog.bodies_count": "{count} أجسام",
|
||||
"dialog.clearance_invalid": "يجب أن يكون الخلوص طولًا قيمته 0 أو أكثر",
|
||||
"dialog.compute": "احسب",
|
||||
@@ -1376,7 +1385,7 @@
|
||||
"dialog.selection_external_curve": "{sketch} · منحنى {id}",
|
||||
"dialog.selection_guidance": "حدد مُدخلًا لتغييره. انقر على العناصر المحددة لإزالتها؛ Ctrl/Cmd مع النقر يبدّل تحديدها أيضًا. راجع المعاينة ثم اضغط موافق.",
|
||||
"dialog.selection_midpoint": "نقطة المنتصف · {id}",
|
||||
"dialog.selection_profile": "{sketch} · المقطع {number}",
|
||||
"dialog.selection_profile": "{sketch} · مقطع {number}",
|
||||
"dialog.selection_step": "{step} من {total}: حدد {input}",
|
||||
"dialog.step": "الخطوة",
|
||||
"document.changed_on_disk": "تغيّر {name} على القرص",
|
||||
@@ -1423,6 +1432,14 @@
|
||||
"file_load.reading_percent": "قراءة الملف... {percent}%",
|
||||
"file_load.rebuilding": "إعادة بناء الميزة {done} من {total}",
|
||||
"file_load.recovering": "استعادة التصميم",
|
||||
"file_load.step_build": "جارٍ بناء النموذج",
|
||||
"file_load.step_feature": "الميزة {done} من {total}",
|
||||
"file_load.step_part": "الجزء {done} من {total}",
|
||||
"file_load.step_percent": "{percent}%",
|
||||
"file_load.step_read": "جارٍ قراءة الملف",
|
||||
"file_load.step_rebuild": "جارٍ إعادة بناء الميزات",
|
||||
"file_load.step_repair": "جارٍ فحص الأجزاء وإصلاحها",
|
||||
"file_load.step_show": "جارٍ تجهيز العرض",
|
||||
"file_load.stopping": "انتظار توقف العملية السابقة...",
|
||||
"file_load.updating_links": "تحديث الروابط",
|
||||
"file_load.waiting": "انتظار عملية الملف",
|
||||
@@ -1430,7 +1447,7 @@
|
||||
"from the profile": "من المقطع",
|
||||
"from the rule": "من القاعدة",
|
||||
"help.apache": "رخصة Apache 2.0",
|
||||
"help.appearance.assign": "يمنح الأجسام أو الأوجه أو المكونات المحددة لونًا أو مظهر مادة، من دون تغيير وزنها. استخدمه للتمييز بين القطع أو لمعاينة التشطيب.",
|
||||
"help.appearance.assign": "يمنح الأجسام أو الأوجه أو المكوّنات المحددة لونًا أو مظهر مادة، من دون تغيير وزنها. استخدمه للتمييز بين القطع أو لمعاينة التشطيب.",
|
||||
"help.assembly.bom": "يسرد قطع التصميم أو المكوّن مع أعدادها حسب رقم القطعة، ويمكنه حفظ القائمة بصيغة CSV أو JSON. استخدمه كقائمة قطع للطلب والتجميع.",
|
||||
"help.attributions": "الإسنادات (ATTRIBUTION.md)",
|
||||
"help.build": "الإصدار {version} · الإيداع {commit} · بُني في {date}",
|
||||
@@ -1717,6 +1734,7 @@
|
||||
"help.timeline.delete": "يحذف الميزات المحددة من السجل، وحذف رسمٍ يحذف معه الميزات المبنية عليه. استخدمه لإزالة خطوة لم تعد مطلوبة.",
|
||||
"help.timeline.dependents": "يسرد الميزات التي ستُحذف مع ميزة ما، وتلك التي ستفشل بدونها. استخدمه قبل حذف ميزة أو تغييرها.",
|
||||
"help.timeline.play": "أعد تشغيل الميزات المتبقية واحدة تلو الأخرى، مع توقف لمدة نصف ثانية بعد كل إعادة بناء. شغّل هذا الأمر مرة أخرى أو اضغط Escape للإيقاف؛ ينتقل «الذهاب إلى النهاية» فورًا إلى النهاية.",
|
||||
"help.version": "الإصدار {version}",
|
||||
"help.view.apply": "يدير العرض إلى اتجاه قياسي مثل الأمامي أو العلوي أو الأيزومتري، أو إلى عرض مسمّى محفوظ. استخدمه للنظر إلى النموذج من زاوية ثابتة.",
|
||||
"help.view.delete": "يحذف عرضًا مسمّى محفوظًا. لا يتغير النموذج.",
|
||||
"help.view.fit": "يضبط التكبير ويُزيح العرض حتى يملأ النموذج كله أو التحديد منطقة العرض. استخدمه حين يخرج النموذج من مجال الرؤية.",
|
||||
@@ -1804,6 +1822,27 @@
|
||||
"home.start_hint": "أنشئ تصميمًا، أو افتح ملفًا حديثًا، أو استكشف مثالًا.",
|
||||
"home.whats_new": "ما الجديد",
|
||||
"home.yesterday": "أمس",
|
||||
"import_report.details": "التفاصيل:",
|
||||
"import_report.file_one": "كانت هناك مشكلة أخرى واحدة في الملف.",
|
||||
"import_report.file_other": "كانت هناك {count} مشكلات أخرى في الملف.",
|
||||
"import_report.hint": "قد يحل ذلك تصدير هذه الأجزاء مرة أخرى من البرنامج الذي أنشأها. يحتوي «نسخ رسالة التحذير» على التفاصيل.",
|
||||
"import_report.left_out_one": "تعذّر إصلاح وجه واحد وهو مفقود.",
|
||||
"import_report.left_out_other": "تعذّر إصلاح {count} أوجه وهي مفقودة.",
|
||||
"import_report.needs_repair_one": "يحتاج جزء واحد إلى إصلاح قبل النمذجة عليه.",
|
||||
"import_report.needs_repair_other": "تحتاج {count} أجزاء إلى إصلاح قبل النمذجة عليها.",
|
||||
"import_report.not_imported_one": "تعذّر استيراد جزء واحد.",
|
||||
"import_report.not_imported_other": "تعذّر استيراد {count} أجزاء.",
|
||||
"import_report.not_shown_one": "تعذّر رسم وجه واحد وهو مخفي.",
|
||||
"import_report.not_shown_other": "تعذّر رسم {count} أوجه وهي مخفية.",
|
||||
"import_report.open_one": "في جزء واحد فجوات بين أوجهه، فاستُورد كجسم مفتوح.",
|
||||
"import_report.open_other": "في {count} أجزاء فجوات بين أوجهها، فاستُوردت كأجسام مفتوحة.",
|
||||
"import_report.refused_one": "تعذّر تحويل جزء واحد إلى مجسم، فلم يُستورد.",
|
||||
"import_report.refused_other": "تعذّر تحويل {count} أجزاء إلى مجسمات، فلم تُستورد.",
|
||||
"import_report.repaired_one": "أُصلحت عيوب صغيرة في جزء واحد.",
|
||||
"import_report.repaired_other": "أُصلحت عيوب صغيرة في {count} أجزاء.",
|
||||
"import_report.solids": "استُورد {solids} من {parts} أجزاء كمجسمات.",
|
||||
"import_report.surfaces_one": "تعذّر تحويل جزء واحد إلى مجسم، فاستُورد كأسطح.",
|
||||
"import_report.surfaces_other": "تعذّر تحويل {count} أجزاء إلى مجسمات، فاستُوردت كأسطح.",
|
||||
"install.button_tip": "ثبّت SolidCraft كتطبيق: يُفتح في نافذته الخاصة ويعمل دون اتصال",
|
||||
"install.dock_1": "اختر ملف › إضافة إلى Dock.",
|
||||
"install.dock_2": "انقر إضافة.",
|
||||
@@ -1828,7 +1867,18 @@
|
||||
"issue.note_save_failed": "تعذّر حفظ لقطة الشاشة: {error}",
|
||||
"issue.note_saved": "حُفظت لقطة الشاشة. أرفق ملف PNG في مسودة GitHub.",
|
||||
"issue.save_screenshot": "حفظ لقطة الشاشة…",
|
||||
"joint.find_limits": "إيجاد الحدود",
|
||||
"joint.find_limits_failed": "توقف البحث عن الحدود بسبب خطأ",
|
||||
"joint.find_limits_hint": "يملأ الحدود من الموضع الذي تلمس فيه القطع التي يحرّكها هذا المفصل قطعةً أخرى لأول مرة، بالدوران أو الانزلاق في الاتجاهين من موضعه الحالي. يجري البحث على نسخة: لا يتغيّر شيء حتى الضغط على OK.",
|
||||
"joint.find_limits_stale": "تغيّر المفصل أو التصميم منذ إيجاد هذه الحدود: ابحث عنها مرة أخرى",
|
||||
"joint.finding_limits": "جارٍ إيجاد الحدود…",
|
||||
"joint.from_bodies_hint": "ينتقل كل جسم مختار إلى مكوّن جديد خاص به ويبقى في مكانه.",
|
||||
"joint.limit_contact": "{side} {value}: {a} يلمس {b}",
|
||||
"joint.limit_limit": "{side} {value}: حدّ قيمة أخرى أو مفصل آخر",
|
||||
"joint.limit_loop": "{side} {value}: لا يمكن لحلقة مغلقة أن تنغلق بعده",
|
||||
"joint.limit_range": "{side}: لا تلامس ضمن {range}، تُرك كما كان",
|
||||
"joint.limit_turn": "{side} {value}: لا تلامس ضمن دورة واحدة",
|
||||
"joint.limits_overlapping": "متداخلة بالفعل هنا: {pairs}. لا توقف البحث إلا إذا تداخلت أعمق.",
|
||||
"joint.one_body": "كلا الاختيارين على {body}. اختر الثاني على قطعة أخرى.",
|
||||
"joint.one_component": "هذه القطع أجسام لمكوّن واحد: {bodies}. المفصل يحرّك مكوّنًا مقابل آخر، لذا تحتاج كل قطعة إلى مكوّن خاص بها.",
|
||||
"joint.rigid_group_one_component": "هذه الأجسام وعددها {count} كلها في مكوّن واحد (أو في التصميم نفسه): استخدم {command} أولًا.",
|
||||
@@ -2091,12 +2141,19 @@
|
||||
"parts.family.button_head_cap_screw": "برغي برأس زرّي بتجويف سداسي",
|
||||
"parts.family.dowel_pin": "مسمار توجيه أسطواني",
|
||||
"parts.family.hex_bolt": "برغي سداسي الرأس",
|
||||
"parts.family.hex_cap_screw_inch": "برغي سداسي الرأس (بوصة)",
|
||||
"parts.family.hex_nut": "صامولة سداسية",
|
||||
"parts.family.hex_nut_inch": "صامولة سداسية (بوصة)",
|
||||
"parts.family.set_screw": "برغي تثبيت بتجويف سداسي، طرف مسطح",
|
||||
"parts.family.socket_head_cap_screw": "برغي برأس أسطواني بتجويف سداسي",
|
||||
"parts.family.washer": "حلقة مسطحة",
|
||||
"parts.family.washer_sae": "حلقة مسطحة، SAE (ضيقة)",
|
||||
"parts.family.washer_uss": "حلقة مسطحة، USS (عريضة)",
|
||||
"parts.holes": "الثقوب (اختياري)",
|
||||
"parts.placement_hint": "اختر ثقبًا أو أكثر لتثبيت قطعة في كل منها، أو اضغط موافق لوضعها عند نقطة الأصل",
|
||||
"parts.system": "نظام الوحدات",
|
||||
"parts.system.inch": "بوصة",
|
||||
"parts.system.metric": "متري",
|
||||
"pattern.alignment_2": "المحاذاة 2",
|
||||
"pattern.alignment_hint": "العدد يشمل الأصل. «توسيط» يوازن النسخ؛ و«قلب» يختار الجانب الإضافي عندما يكون العدد زوجيًا.",
|
||||
"pattern.direction_1": "الاتجاه 1",
|
||||
@@ -2135,6 +2192,18 @@
|
||||
"prefs.alt_frees_manipulators": "Alt / Option يحرر أسهم أدوات التحريك أيضًا",
|
||||
"prefs.anti_aliasing_amd_dx12": "يستخدم تنعيم الحواف 1× لأن فحص بدء التشغيل لم يرسم شيئًا مع أخذ العينات المتعددة ونجح عند 1×. يمكنك اختيار إعداد آخر أعلاه؛ أعد التشغيل لتطبيقه إن نجح الفحص.",
|
||||
"prefs.anti_aliasing_note": "ينعّم الحواف المسننة.",
|
||||
"prefs.appearance.colour_vision_help": "يغيّر الألوان ذات الدلالة: حالات الرسم والمحاور والثنيات والتحليلات والأيقونات.",
|
||||
"prefs.appearance.desktop_only": "لسطح المكتب فقط",
|
||||
"prefs.appearance.follow_help": "يعيد التحميل عندما تعيد أداة أخرى كتابة الملف.",
|
||||
"prefs.appearance.interface_scale": "مقياس الواجهة",
|
||||
"prefs.appearance.next_themes": "السمات التالية",
|
||||
"prefs.appearance.option_count": "{count} خيارات",
|
||||
"prefs.appearance.option_count_one": "{count} خيار",
|
||||
"prefs.appearance.previous_themes": "السمات السابقة",
|
||||
"prefs.appearance.theme_count": "{count} سمات",
|
||||
"prefs.appearance.theme_own": "ألوان السمة",
|
||||
"prefs.appearance.viewport_colours": "ألوان العرض ثلاثي الأبعاد",
|
||||
"prefs.appearance.viewport_logo": "منفذ العرض والشعار",
|
||||
"prefs.applies_on_restart": "يُطبَّق عند إعادة التشغيل",
|
||||
"prefs.assistant": "المساعد",
|
||||
"prefs.auto_operation": "اختيار العملية تلقائيًا",
|
||||
@@ -2149,6 +2218,8 @@
|
||||
"prefs.bottom_centre": "أسفل الوسط",
|
||||
"prefs.bottom_right": "أسفل اليمين",
|
||||
"prefs.browser": "المستعرض",
|
||||
"prefs.browser_components_open": "المكوّنات مفتوحة من البداية",
|
||||
"prefs.browser_components_open_note": "يبدأ كل مكوّن في المستعرض مفتوحًا كما كان من قبل، بدلًا من أن يبقى مغلقًا حتى تفتحه.",
|
||||
"prefs.browser_eye_left": "أيقونة الإظهار بجانب سهم الطي",
|
||||
"prefs.browser_eye_left_note": "تعرض صفوف المستعرض أيقونة الإظهار/الإخفاء بجانب سهم الطي كما كانت من قبل، بدلًا من عمود عند نهاية الصفوف.",
|
||||
"prefs.browser_reveal_on_hover": "إظهار عند مرور المؤشر",
|
||||
@@ -2198,7 +2269,19 @@
|
||||
"prefs.font_sample": "نص تجريبي للخط · Ø12.50 mm · 45.0° · 0123456789",
|
||||
"prefs.font_unreadable": "تعذّرت قراءة الخط المحدد.",
|
||||
"prefs.general": "عام",
|
||||
"prefs.general_note": "اللغة والحفظ والتحديثات.",
|
||||
"prefs.general.active": "نشط",
|
||||
"prefs.general.crash_reports": "تقارير الأعطال",
|
||||
"prefs.general.edited": "معدّل",
|
||||
"prefs.general.held": "المشمول",
|
||||
"prefs.general.import": "استيراد...",
|
||||
"prefs.general.last_check": "آخر تحقق: {time}",
|
||||
"prefs.general.minutes": "دقائق",
|
||||
"prefs.general.name_setup": "سمِّ هذا الإعداد",
|
||||
"prefs.general.profile_name": "اسم ملف التعريف",
|
||||
"prefs.general.save_new": "حفظ كجديد",
|
||||
"prefs.general.step_import": "STEP والاستيراد",
|
||||
"prefs.general.this_computer": "هذا الحاسوب",
|
||||
"prefs.general_note": "ملفات التعريف واللغة والملفات والتحديثات.",
|
||||
"prefs.gesture_orbit": "تدوير العرض",
|
||||
"prefs.gesture_pan": "إزاحة العرض",
|
||||
"prefs.gesture_zoom": "تكبير",
|
||||
@@ -2255,10 +2338,10 @@
|
||||
"prefs.logo_accent": "لون التمييز",
|
||||
"prefs.logo_theme": "ألوان السمة",
|
||||
"prefs.max_file_size": "أكبر ملف يمكن فتحه",
|
||||
"prefs.max_file_size_design": "ملفات التصميم",
|
||||
"prefs.max_file_size_design": "أكبر ملف للفتح: التصميم",
|
||||
"prefs.max_file_size_flag": "بدأ هذا التشغيل بالخيار --max-file-size {size}، ويسري حتى إغلاق SolidCraft. تسري هذه الإعدادات عند تشغيله دون هذا الخيار.",
|
||||
"prefs.max_file_size_note": "القيم الافتراضية: 512 MB و256 MB؛ الحد الأقصى 16 GB. يتطلب فتح ملف ذاكرة تعادل حجمه عدة مرات. قد يحتاج التصميم الذي يحتفظ باستيراد كبير إلى حد أعلى لملف التصميم لحفظه وإعادة فتحه.",
|
||||
"prefs.max_file_size_step": "ملفات STEP والاستيراد",
|
||||
"prefs.max_file_size_step": "أكبر ملف للفتح: STEP والاستيراد",
|
||||
"prefs.max_file_size_web": "يفتح تطبيق الويب ملفات STEP والاستيراد حتى 512 MB والتصاميم حتى 256 MB؛ لا يتسع تبويب المتصفح لملفات أكبر. يمكن رفع هذه الحدود في تطبيق سطح المكتب.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "أيقونات كبيرة",
|
||||
@@ -2275,7 +2358,7 @@
|
||||
"prefs.navigator_fit_note": "النقر على وجه أو ركن أو سهم في المكعب، أو على محور في أداة المحاور، يوجّه العرض أيضًا نحو منتصف النموذج من المسافة الواحدة التي تُظهره كاملًا من كل جهة (كرته المحيطة). يستخدم العرض الرئيسي التكبير والمركز ذاتهما ما دام هذا مفعّلًا، فلا تغيّر العروض المختارة بعده سوى الاتجاه. عند الإيقاف: يدور العرض في مكانه محتفظًا بالهدف والتكبير.",
|
||||
"prefs.network_host_remove": "إزالة",
|
||||
"prefs.network_locations": "السماح بمواقع الشبكة في التصاميم",
|
||||
"prefs.network_locations_note": "عند الإيقاف: الملفات المرتبطة بالتصميم على حاسوب آخر (\\\\server\\share) لا تُفتح إلا من الخوادم المدرجة هنا، أو بعد أن تسمح لأحدها عند السؤال.",
|
||||
"prefs.network_locations_note": "للمؤسسات التي تحفظ التصاميم على خوادم الملفات. عند الإيقاف: الخوادم التي تسمح بها فقط.",
|
||||
"prefs.network_locations_section": "مواقع الشبكة",
|
||||
"prefs.not_on_gpu": "غير متاح على هذا المعالج الرسومي",
|
||||
"prefs.on": "تشغيل",
|
||||
@@ -2301,10 +2384,12 @@
|
||||
"prefs.profile_applied": "تم تطبيق ملف التعريف.",
|
||||
"prefs.profile_changed": "تغيّرت الإعدادات منذ حفظ ملف التعريف. «حفظ» يحفظها فيه.",
|
||||
"prefs.profile_deleted": "تم حذف ملف التعريف.",
|
||||
"prefs.profile_export": "تصدير…",
|
||||
"prefs.profile_export": "تصدير",
|
||||
"prefs.profile_exported": "تم تصدير ملف التعريف.",
|
||||
"prefs.profile_imported": "تم استيراد ملف التعريف. اختره في «ملف التعريف النشط» للتبديل إليه.",
|
||||
"prefs.profile_imported": "تم استيراد ملف التعريف.",
|
||||
"prefs.profile_name_hint": "اسم ملف التعريف، مثل: حاسوب محمول",
|
||||
"prefs.profile_operations": "تحليل زمن العمليات",
|
||||
"prefs.profile_operations_note": "يسجّل المدة التي تستغرقها استدعاءات SMLib وعمليات النواة والميزات والأوامر وعمليات الاستيراد. يقرؤه الوكلاء عبر diagnostics.profile.",
|
||||
"prefs.profile_rename": "إعادة تسمية",
|
||||
"prefs.profile_renamed": "تمت إعادة تسمية ملف التعريف.",
|
||||
"prefs.profile_save": "حفظ",
|
||||
@@ -2323,16 +2408,20 @@
|
||||
"prefs.render_previous_geometry_budget": "استخدام ميزانية الهندسة السابقة لمعاينة التصيير",
|
||||
"prefs.render_previous_geometry_budget_help": "استعادة حد الهندسة الثابت السابق (512 ميبيبايت على سطح المكتب 64 بت، و128 ميبيبايت على المنصات الأخرى) بدلًا من الميزانية المعتمدة على الجهاز.",
|
||||
"prefs.reports_saved": "{n} محفوظة",
|
||||
"prefs.restore_page_defaults": "استعادة الإعدادات الافتراضية لهذه الصفحة",
|
||||
"prefs.ribbon_click_ends_tool": "النقر على زر الأداة قيد التشغيل ينهيها",
|
||||
"prefs.ribbon_click_ends_tool_note": "النقر على زر الشريط المضيء لأداة الرسم المستخدمة ينهي الأداة، فيعود النقر في العرض إلى التحديد. عند الإيقاف: يبدأ النقر الأداة من جديد.",
|
||||
"prefs.ribbon_reveal": "إظهار مكان نتائج البحث في الشريط",
|
||||
"prefs.ribbon_reveal_note": "عند تمرير المؤشر فوق نتيجة في «البحث في الأوامر» يظهر مكان الأمر في الشريط: تبويبه أو زره أو صفه في القائمة المنسدلة للوحة. ويومض هناك الأمر الذي بدأ من البحث، ويُعلِّم الأمر الجاري من قائمة منسدلة لوحتَه.",
|
||||
"prefs.right_click": "نقر أيمن",
|
||||
"prefs.saving": "الحفظ التلقائي والاستعادة",
|
||||
"prefs.saving": "البدء والحفظ",
|
||||
"prefs.scale_note": "أو Ctrl + / Ctrl − في النافذة. الأزرار 28 px عند 100%.",
|
||||
"prefs.scale_text": "الحجم والنص",
|
||||
"prefs.search": "البحث في الإعدادات",
|
||||
"prefs.search_empty": "لا توجد إعدادات مطابقة",
|
||||
"prefs.section_hatch": "تهشير المقطع",
|
||||
"prefs.section_hatch_by_part": "حسب الجزء",
|
||||
"prefs.section_hatch_one": "نمط واحد",
|
||||
"prefs.select": "تحديد",
|
||||
"prefs.select_one_item": "تحديد عنصر واحد",
|
||||
"prefs.select_other_hidden": "تحديد العناصر الهندسية المخفية باستخدام «تحديد عنصر آخر»",
|
||||
@@ -2447,6 +2536,12 @@
|
||||
"prefs.under_cursor": "تحت المؤشر",
|
||||
"prefs.units_precision": "الوحدات والدقة",
|
||||
"prefs.updates": "التحديثات",
|
||||
"prefs.validation": "التحقق من الأجسام",
|
||||
"prefs.validation.each_feature": "مرة لكل ميزة",
|
||||
"prefs.validation.each_step": "بعد كل خطوة",
|
||||
"prefs.validation.label": "فحص النواة الكامل",
|
||||
"prefs.validation.off": "متوقف (تجريبي)",
|
||||
"prefs.validation_note": "بعد كل خطوة: يفحص كل عملية سحب ودمج وتقويس.\nمرة لكل ميزة: يفحص ناتج كل ميزة مرة واحدة.\nمتوقف (تجريبي): لا فحص.",
|
||||
"prefs.value_boxes_in_view": "إظهار القيم بجانب مقابضها في العرض",
|
||||
"prefs.value_boxes_in_view_tip": "قيمة الأمر (المسافة، نصف القطر…) في مربع بجانب مقبضه، فوق النموذج. عند الإيقاف: تكتبها في نافذة الأمر، التي تتلقى لوحة المفاتيح بعد التحديد، ولا شيء يغطي النموذج.",
|
||||
"prefs.view": "سلوك العرض",
|
||||
@@ -2481,6 +2576,17 @@
|
||||
"recovery.just_now": "الآن",
|
||||
"recovery.min_ago": "قبل {count} دقيقة",
|
||||
"recovery.never_saved": "لم يُحفظ قط",
|
||||
"refused.change": "تعذّر تغيير {body}.",
|
||||
"refused.cut": "تعذّر قطع {body}.",
|
||||
"refused.face_curved": "أحد أوجهه المنحنية لا يمكن استخدامه في النمذجة.",
|
||||
"refused.face_flat": "أحد أوجهه المستوية لا يمكن استخدامه في النمذجة.",
|
||||
"refused.face_freeform": "أحد أوجهه الحرة لا يمكن استخدامه في النمذجة.",
|
||||
"refused.face_rounded": "أحد أوجهه المستديرة لا يمكن استخدامه في النمذجة.",
|
||||
"refused.faces_apart": "أوجهه لا تلتقي بدقة كافية لاستخدامها في النمذجة.",
|
||||
"refused.intersect": "تعذّر التقاطع مع {body}.",
|
||||
"refused.join": "تعذّر الضم إلى {body}.",
|
||||
"refused.repair": "إصلاح الجزء",
|
||||
"refused.show_face": "إظهار الوجه",
|
||||
"render.adaptive_absolute": "الحد الأدنى المطلق لخطأ RGB",
|
||||
"render.adaptive_coverage": "هدف خطأ التغطية",
|
||||
"render.adaptive_enable": "توزيع العيّنات باستخدام عيّنات استكشافية مستقلة",
|
||||
@@ -3948,11 +4054,20 @@
|
||||
"the section's profiles must be in one sketch": "يجب أن تكون مقاطع المقطع العرضي في رسم واحد",
|
||||
"the two origins are on the same component": "يقع الأصلان على المكوّن نفسه",
|
||||
"thread.automatic_size": "حجم تلقائي",
|
||||
"thread.chamfer_depth_needed": "مطلوب لهذا الشكل",
|
||||
"thread.designation": "التسمية",
|
||||
"thread.direction": "اتجاه السن اللولبي",
|
||||
"thread.drill_range": "مثقاب صانع اللقمة {drill} مم. القطر الأصغر القياسي للسن الداخلي {minor} مم. يُقبل مثقاب من {low} إلى {high} مم.",
|
||||
"thread.drill_range_custom": "المثقاب هو القطر الأكبر ناقص الخطوة، {drill} مم. القطر الأصغر القياسي للسن الداخلي {minor} مم. يُقبل مثقاب من {low} إلى {high} مم.",
|
||||
"thread.drill_range_taper": "مثقاب صانع اللقمة {drill} مم. القطر الأصغر القياسي عند نهاية السن {minor} مم. يُقبل مثقاب من {low} إلى {high} مم.",
|
||||
"thread.end_square": "قائمة",
|
||||
"thread.fade_short": "1.25 P (ISO 3508)",
|
||||
"thread.fade_standard": "2.5 P (ISO 3508)",
|
||||
"thread.fits_the_face": "يناسب الوجه، مثلاً {example}",
|
||||
"thread.form_bspp": "سن أنابيب G (ISO 228-1)",
|
||||
"thread.form_bspt": "سن أنابيب مخروطي R، Rc (ISO 7-1)",
|
||||
"thread.form_npt": "سن أنابيب مخروطي NPT (ASME B1.20.1)",
|
||||
"thread.form_rp": "سن أنابيب داخلي متوازٍ Rp (ISO 7-1)",
|
||||
"thread.iso_metric": "متري ISO",
|
||||
"thread.left_hand": "يساري",
|
||||
"thread.right_hand": "يميني",
|
||||
"thread.select_size": "اختر الحجم",
|
||||
"timeline.delete_after_marker_one": "حذف ميزة واحدة بعد مؤشر السجل",
|
||||
"timeline.delete_after_marker_other": "حذف {count} ميزات بعد مؤشر السجل",
|
||||
@@ -4126,6 +4241,7 @@
|
||||
"ui.agent_chat_auth_terminal": "شغّل هذا في الطرفية، ثم اضغط «اتصال» مرة أخرى:",
|
||||
"ui.agent_chat_auth_title": "تسجيل الدخول إلى الوكيل",
|
||||
"ui.agent_chat_cancel": "إلغاء",
|
||||
"ui.agent_chat_cancel_timeout": "لم يستجب الوكيل لطلب الإيقاف، فتم قطع الاتصال به. أعد الاتصال للمتابعة.",
|
||||
"ui.agent_chat_cannot_start": "مثبّت، لكنه لا يبدأ: {error}",
|
||||
"ui.agent_chat_changes": "التغييرات: {n}، خطوة تراجع واحدة",
|
||||
"ui.agent_chat_changes_n": "{n} تغييرات",
|
||||
@@ -4203,6 +4319,7 @@
|
||||
"ui.agent_chat_no_images": "هذا الوكيل لا يقبل الصور",
|
||||
"ui.agent_chat_no_picture": "لا توجد صورة في الحافظة",
|
||||
"ui.agent_chat_no_tool_call": "لم يستخدم النموذج أدوات SolidCraft، لذا لم يتغيّر التصميم. اختر نموذجًا يدعم استدعاء الأدوات؛ راجع «النماذج المحلية» في دليل محادثة الوكيل.",
|
||||
"ui.agent_chat_node_too_old": "إصدار Node.js {version} قديم جدًا لهذا الوكيل. ثبّت Node.js 20.10 أو أحدث، ثم اضغط على تحديث.",
|
||||
"ui.agent_chat_none_found": "لم يُعثر على وكيل برمجي. ثبّت أحد هذه ثم اضغط تحديث:",
|
||||
"ui.agent_chat_none_found_short": "لم يُعثر على وكيل",
|
||||
"ui.agent_chat_not_installed": "غير مثبت",
|
||||
@@ -4422,6 +4539,8 @@
|
||||
"ui.centerline": "خط المركز",
|
||||
"ui.chain": "سلسلة",
|
||||
"ui.chamfer": "شطف",
|
||||
"ui.chamfer_angle": "زاوية الشطف",
|
||||
"ui.chamfer_depth": "عمق الشطف",
|
||||
"ui.chamfer_type": "نوع الشطف",
|
||||
"ui.change_workspace": "تغيير مساحة العمل",
|
||||
"ui.circle": "دائرة",
|
||||
@@ -4452,10 +4571,16 @@
|
||||
"ui.colour_vision_sample": "عيّنة",
|
||||
"ui.colour_vision_tritan": "ضعف الأزرق",
|
||||
"ui.colour_vision_tritan_tip": "لضعف أو غياب الإحساس باللون الأزرق",
|
||||
"ui.comb_length": "طول المشط",
|
||||
"ui.comb_length_tip": "طول الأسنان: مضاعف للطول التلقائي الذي يجعل أطول سنّ خُمس حجم المنحنيات المعروضة.",
|
||||
"ui.comb_teeth": "الأسنان",
|
||||
"ui.comb_teeth_tip": "عدد أسنان كل مشط، موزّعة بالتساوي على طول المنحنى. كلما زادت الأسنان ظهرت تفاصيل أكثر عن تغيّر الانحناء.",
|
||||
"ui.combine": "دمج",
|
||||
"ui.coming_later": "قريبًا",
|
||||
"ui.command_done": "تم {command}",
|
||||
"ui.command_palette_hint": "اكتب أمرًا، مثل {example} — أو اضغط S للبحث",
|
||||
"ui.components_from_bodies_hint": "أنشئ مكوّنًا لكل جسم محدد، باسم ذلك الجسم، ضمن المكوّن النشط.",
|
||||
"ui.components_from_bodies_required": "حدد جسمًا واحدًا على الأقل.",
|
||||
"ui.concentric": "متحد المركز",
|
||||
"ui.cone": "مخروط",
|
||||
"ui.conic_curve": "منحنى مخروطي",
|
||||
@@ -4474,11 +4599,24 @@
|
||||
"ui.construction.ribbon_label": "خطوط مساعدة",
|
||||
"ui.construction.side_hint": "خط إنشائي: انقر الجهة التي يمتد نحوها الخط؛ انقر بزر الفأرة الأيمن للبدء من جديد",
|
||||
"ui.context_menu": "القائمة السياقية",
|
||||
"ui.continuity_crease": "تجعّد",
|
||||
"ui.continuity_curvature": "الانحناء",
|
||||
"ui.continuity_curvature_change": "تغيّر الانحناء",
|
||||
"ui.continuity_curvature_change_tip": "تغيّر الانحناء (G3): يتغير الانحناء أيضًا بسلاسة عبر خط الالتقاء.",
|
||||
"ui.continuity_curvature_tip": "متصل الانحناء (G2): تمر الانعكاسات دون انكسار.",
|
||||
"ui.continuity_g0": "G0 تجعّد",
|
||||
"ui.continuity_g1": "G1 مماس",
|
||||
"ui.continuity_g2": "G2 انحناء",
|
||||
"ui.continuity_gap": "فجوة",
|
||||
"ui.continuity_position": "الموضع",
|
||||
"ui.continuity_position_tip": "الموضع (G0): تلتقي الأسطح، مع تجعّد عند التقائها.",
|
||||
"ui.continuity_smooth_curvature_change": "تغيّر انحناء سلس",
|
||||
"ui.continuity_smooth_curvature_change_tip": "تغيّر انحناء سلس (G4): حتى طريقة تغيّر الانحناء تستمر بسلاسة عبر خط الالتقاء.",
|
||||
"ui.continuity_tangent": "مماس",
|
||||
"ui.continuity_tangent_tip": "مماس (G1): لا تجعّد، لكن الانحناء يقفز عبر خط الالتقاء.",
|
||||
"ui.copy": "نسخ",
|
||||
"ui.copy_details": "نسخ التفاصيل",
|
||||
"ui.copy_details_tip": "انسخ الرسالة كاملة للبحث عنها أو للإبلاغ عنها.",
|
||||
"ui.corner": "زاوية",
|
||||
"ui.corners": "الزوايا",
|
||||
"ui.cosmetic_thread_markings": "علامات السن اللولبي الشكلية",
|
||||
@@ -4500,7 +4638,7 @@
|
||||
"ui.dark": "داكن",
|
||||
"ui.default_modelling_orientation": "اتجاه النمذجة الافتراضي",
|
||||
"ui.delete": "حذف",
|
||||
"ui.delete_select_first": "حدّد أولًا ما تريد حذفه: الميزات أو الأجسام أو المكونات أو هندسة الرسم",
|
||||
"ui.delete_select_first": "حدّد أولًا ما تريد حذفه: الميزات أو الأجسام أو المكوّنات أو هندسة الرسم",
|
||||
"ui.depth": "العمق",
|
||||
"ui.design": "التصميم",
|
||||
"ui.design_properties.author": "المؤلف",
|
||||
@@ -4596,8 +4734,10 @@
|
||||
"ui.edit_sketch_empty": "لا توجد رسومات لتعديلها",
|
||||
"ui.ellipse": "قطع ناقص",
|
||||
"ui.emboss_wrap_note": "تلتف ملامح الخطوط المتعامدة المماسة لأسطوانة مدعومة تلقائيًا. اختر جدارًا لتحديد الهدف.",
|
||||
"ui.end": "النهاية",
|
||||
"ui.end_radius": "نصف قطر النهاية",
|
||||
"ui.end_scale": "مقياس النهاية",
|
||||
"ui.ends": "الطرفان",
|
||||
"ui.entities": "العناصر",
|
||||
"ui.equal": "متساوٍ",
|
||||
"ui.equation_end": "النهاية",
|
||||
@@ -4617,6 +4757,7 @@
|
||||
"ui.export_ellipsis_keymap": "تصدير…",
|
||||
"ui.export_keymap_tip": "المفاتيح ومفضلات مربع S وشريط الأدوات، كملف",
|
||||
"ui.extend": "تمديد",
|
||||
"ui.extent": "الامتداد",
|
||||
"ui.extent_type": "نوع الامتداد",
|
||||
"ui.extract_component": "استخراج إلى ملف…",
|
||||
"ui.extract_design_changed": "تغيّر التصميم أثناء اختيار الملف. ابدأ الاستخراج إلى ملف من جديد.",
|
||||
@@ -4625,11 +4766,39 @@
|
||||
"ui.extrude_auto_to_object": "البثق إلى هدف مختار",
|
||||
"ui.extrude_auto_to_object_tip": "في البثق، مع اختيار مقطع، يؤدي اختيار وجه أو رأس أو مستوى موازٍ خارج مستواه إلى تبديل الامتداد إلى «إلى كائن»",
|
||||
"ui.factor": "المعامل",
|
||||
"ui.fade": "تلاشٍ",
|
||||
"ui.fade_length": "طول التلاشي",
|
||||
"ui.feature_history": "سجل الميزات",
|
||||
"ui.feature_options": "خيارات الميزة",
|
||||
"ui.features": "الميزات",
|
||||
"ui.file": "ملف",
|
||||
"ui.fillet": "تقويس",
|
||||
"ui.fillet_corner_ball": "كرة",
|
||||
"ui.fillet_corner_ball_tip": "جزء من سطح الكرة المتدحرجة، مماسّ (G1) لكل تقويس.",
|
||||
"ui.fillet_corner_curvature_tip": "الانحناء (G2): رقعة ناعمة. تُرجَع الزاوية على طول كل حافة بمقدار الإرجاع ({factor} ضعف نصف القطر ما لم تكتب قيمة) وتُملأ برقعة، فتمر الانعكاسات عبرها دون انكسار.",
|
||||
"ui.fillet_corner_needs_constant": "زوايا الرقعة الناعمة (G2) تتطلب نصف قطر ثابتًا وملاءمة دقيقة لنصف القطر.",
|
||||
"ui.fillet_corner_patch": "رقعة ناعمة",
|
||||
"ui.fillet_corner_patch_tip": "متصل الانحناء (G2): تُرجَع الزاوية على طول كل حافة وتُملأ برقعة، فتمر الانعكاسات عبرها دون انكسار.",
|
||||
"ui.fillet_corner_tangent_tip": "مماس (G1): زاوية بالكرة المتدحرجة، جزء من سطح الكرة المتدحرجة مماسّ لكل تقويس.",
|
||||
"ui.fillet_error_corner": "تعذّر بناء الزاوية الناعمة هنا. جرّب مسافة إرجاع أخرى، أو زوايا مماسّة.",
|
||||
"ui.fillet_error_failed": "تعذّر بناء هذا التقويس على هذه الحواف. جرّب نصف قطر آخر، أو قوّس حوافّ أقل في المرة الواحدة.",
|
||||
"ui.fillet_error_setback_short": "مسافة الإرجاع قصيرة جدًا على رقعة هذه الزاوية: تُبنى ابتداءً من {least}. جرّب مسافة إرجاع أطول.",
|
||||
"ui.fillet_error_too_large": "نصف القطر كبير جدًا على الوجوه المجاورة لهذه الحواف. جرّب نصف قطر أصغر، أو اختر في «حدود الوجوه» الخيار «أكبر نصف قطر».",
|
||||
"ui.fillet_fit": "حدود الوجوه",
|
||||
"ui.fillet_fit_exact": "نصف قطر دقيق",
|
||||
"ui.fillet_fit_needs_constant": "ملاءمة نصف القطر تتطلب نصف قطر ثابتًا.",
|
||||
"ui.fillet_fit_reduce": "أكبر نصف قطر",
|
||||
"ui.fillet_fit_taper": "نصف قطر مُضيَّق",
|
||||
"ui.fillet_fit_tip": "عندما يكون نصف القطر أكبر مما تسمح به الوجوه المجاورة للحافة.\nنصف قطر دقيق: نصف القطر كما هو، أو لا تقويس ورسالة توضّح السبب.\nأكبر نصف قطر: تُقوَّس كل حافة بأكبر نصف قطر يتّسع.\nنصف قطر مُضيَّق: يبقى نصف القطر، ويُضيَّق التقويس فقط حيث تنتهي الوجوه.",
|
||||
"ui.fillet_options_tip": "سلسلة مماسّة: قوّس أيضًا الحواف التي تتصل بسلاسة بالحواف المختارة.\nمعاينة: اعرض النتيجة في العرض أثناء التحرير.",
|
||||
"ui.fillet_setback": "مسافة الإرجاع",
|
||||
"ui.fillet_setback_hint": "{factor} × نصف القطر",
|
||||
"ui.fillet_setback_least": "أكثر من {least}",
|
||||
"ui.fillet_setback_tip": "حيث تبدأ رقعة الزاوية الناعمة: المسافة على طول كل حافة من الزاوية. الافتراضي {factor} ضعف نصف القطر. يجب أن تكون أكبر من أقل مسافة إرجاع، حيث تلتقي الاستدارات بدونها: نصف القطر حيث تكون الأوجه متعامدة.",
|
||||
"ui.fillet_smooth_radius": "تغيّر ناعم لنصف القطر",
|
||||
"ui.fillet_type_choices_tip": "نصف قطر ثابت: نصف قطر واحد على طول الحافة.\nطول الوتر: يبقى عرض التقويس نفسه مهما كانت الزاوية بين الوجوه.\nنصف قطر متغيّر: نصف قطر عند كل طرف، ومزيد على طول الحافة إن أضفته.\nمتصل الانحناء: مقطع بلا انكسار في الانعكاسات حيث يلتقي بالوجوه المستوية (G2).\nمنحنى: يتبع نصف القطر على طول الحافة أنصاف أقطار تحكّم تحدّدها.",
|
||||
"ui.fillet_type_curvature": "متصل الانحناء",
|
||||
"ui.fillet_type_tip": "المقطع عبر الحافة. متصل الانحناء: مقطع متصل الانحناء (G2) بين وجوه مستوية، فتمر الانعكاسات من الوجوه إلى التقويس دون انكسار.",
|
||||
"ui.filter_commands_or_keys": "تصفية الأوامر أو المفاتيح",
|
||||
"ui.findInTimeline": "البحث في الخط الزمني",
|
||||
"ui.findInWindow": "البحث في النافذة",
|
||||
@@ -4746,7 +4915,7 @@
|
||||
"ui.history_edge_hint": "انقر مرتين على الحافة",
|
||||
"ui.history_first": "الذهاب إلى البداية",
|
||||
"ui.history_forward": "التقدم خطوة",
|
||||
"ui.history_hide_hidden": "إخفاء المكونات المخفية",
|
||||
"ui.history_hide_hidden": "إخفاء المكوّنات المخفية",
|
||||
"ui.history_hide_names": "إخفاء الأسماء",
|
||||
"ui.history_last": "الذهاب إلى النهاية",
|
||||
"ui.history_marker_tip": "مؤشر السجل: اسحبه لإرجاع النموذج، والميزات الجديدة تُضاف هنا",
|
||||
@@ -4756,11 +4925,12 @@
|
||||
"ui.history_play_button_show": "إظهار زر التشغيل",
|
||||
"ui.history_play_finish_action": "أكمل الإجراء الحالي قبل تشغيل السجل",
|
||||
"ui.history_play_roll_back": "أعد مؤشر السجل إلى الخلف قبل التشغيل",
|
||||
"ui.history_show_hidden": "إظهار المكونات المخفية",
|
||||
"ui.history_show_hidden": "إظهار المكوّنات المخفية",
|
||||
"ui.history_show_names": "إظهار الأسماء",
|
||||
"ui.history_sketch_listed_under": "مدرج تحت {feature}",
|
||||
"ui.history_title": "السجل",
|
||||
"ui.history_toggle_names": "إظهار أسماء الميزات أو إخفاؤها",
|
||||
"ui.history_waits_for_editor": "ينتظر التراجع والإعادة والسجل حتى يكتمل فتح محرر الميزة",
|
||||
"ui.hole": "ثقب",
|
||||
"ui.hole_tap_type": "نوع لولبة الثقب",
|
||||
"ui.hole_type": "نوع الثقب",
|
||||
@@ -4778,6 +4948,7 @@
|
||||
"ui.inspect": "فحص",
|
||||
"ui.instances": "النسخ",
|
||||
"ui.intersect": "تقاطع",
|
||||
"ui.iso_4753": "ISO 4753",
|
||||
"ui.isolate": "عزل",
|
||||
"ui.join": "ضم",
|
||||
"ui.joint_play": "▶ تشغيل",
|
||||
@@ -4880,6 +5051,7 @@
|
||||
"ui.part_size_needs_a_part": "حدد قطعة قياسية مُدرجة (برغي أو صامولة أو حلقة مسطحة أو مسمار) لتغيير مقاسها. لتكبير جسم أو تصغيره، استخدم «تحجيم».",
|
||||
"ui.part_size_shared": "يعيد بناء القطعة مستبدلًا أي ميزات مضافة. تتحدث كل نسخ هذا المكوّن.",
|
||||
"ui.paste": "لصق",
|
||||
"ui.patch_continuity_corner_tip": "تلتقي الرقعة بالأوجه المحيطة بها بالاستمرارية المختارة على طول كل جانب. وعند زاوية تلتقي فيها تلك الأوجه بعضها ببعض بانحناءات مختلفة، تبقى مماسّة (G1) هناك.",
|
||||
"ui.path": "المسار",
|
||||
"ui.pattern_object_type_tip": "الأجسام: أشكال مصمتة تُختار في العرض أو في المستعرض؛ يُنسخ كل منها كاملًا.\nالميزات: خطوات في السجل، مثل بثق أو ثقب، تُختار في السجل أو على وجه صنعته؛ تتكرر الخطوة.\nالرسم ليس أيًّا منهما: كرّر منحنياته بأداة النمط أو المرآة داخل الرسم، أو ابثقه أولًا.",
|
||||
"ui.pattern_on_path": "نمط على مسار",
|
||||
@@ -5056,6 +5228,8 @@
|
||||
"ui.sketch_intersect_geometry": "الهندسة",
|
||||
"ui.sketch_intersect_projection_link": "رابط الإسقاط",
|
||||
"ui.sketch_intersect_selection_filter": "مرشح التحديد",
|
||||
"ui.sketch_keys_only": "إبقاء مفاتيح النموذج خارج الرسومات",
|
||||
"ui.sketch_keys_only_tip": "أثناء تحرير رسم، تعمل مفاتيح الرسم والمفاتيح التي تعمل في كل مكان فقط؛ ولا يفعل مفتاح نموذج مثل E أو F شيئًا، فلا يمكن لمفتاح مضغوط بالخطأ أن يغلق رسمًا ثقيلًا.",
|
||||
"ui.sketch_modifiers": "مُعدِّلات الرسم",
|
||||
"ui.sketch_project_geometry": "الهندسة",
|
||||
"ui.sketch_report.all_constrained": "كل شيء مقيد بالكامل.",
|
||||
@@ -5073,7 +5247,7 @@
|
||||
"ui.slicer_no_response": "لم يستجب {slicer} في الوقت المحدد. الملف محفوظ مسبقًا في {path}: أرسله مرة أخرى بعد أن يبدأ برنامج التقطيع.",
|
||||
"ui.slicer_resend_hint": "الإرسال مرة أخرى يستبدل الملف المفتوح في برنامج التقطيع.",
|
||||
"ui.slicer_single_instance": "فتح الملفات في برنامج تقطيع يعمل مسبقًا",
|
||||
"ui.slicer_single_instance_tip": "يحمّل PrusaSlicer وSuperSlicer وCura الملف في النافذة المفتوحة بدلًا من تشغيل نسخة أخرى. أما برامج التقطيع الأخرى فتُشغَّل كما هي. يفعل PrusaSlicer 3.0 هذا دائمًا: لا يقبل مشغّله الملف بأي طريقة أخرى.",
|
||||
"ui.slicer_single_instance_tip": "لبرامج التقطيع التي تفتح الملف في نافذة قيد التشغيل.",
|
||||
"ui.slicer_starting": "جارٍ تشغيل {slicer}…",
|
||||
"ui.slicer_still_starting": "لا يزال الانتظار جاريًا لبدء {slicer}. لم يُرسل أي شيء آخر.",
|
||||
"ui.slicer_stl_fallback": "تعذّرت كتابة ملف 3MF، لذا هذا ملف STL.",
|
||||
@@ -5218,13 +5392,13 @@
|
||||
"update.available": "SolidCraft {version} متاح",
|
||||
"update.button": "تحديث",
|
||||
"update.button_version": "التحديث إلى {version}",
|
||||
"update.check_automatically": "التحقق من التحديثات تلقائيًا",
|
||||
"update.check_automatically": "التحقق عند البدء ومرة يوميًا",
|
||||
"update.check_for_updates": "التحقق من وجود تحديثات",
|
||||
"update.check_now": "تحقق الآن",
|
||||
"update.check_unavailable": "التحقق من التحديثات غير متاح لهذا التثبيت.",
|
||||
"update.checking": "جارٍ التحقق…",
|
||||
"update.copy": "نسخ",
|
||||
"update.dmg_opened": "صورة القرص مفتوحة وموثّقة: اسحب SolidCraft إلى مجلد التطبيقات مستبدلًا النسخة القديمة، ثم أعد تشغيل SolidCraft.",
|
||||
"update.dmg_opened": "صورة القرص مفتوحة وموثّقة: أنهِ SolidCraft، ثم اسحب SolidCraft الجديد إلى مجلد التطبيقات مستبدلًا النسخة القديمة، ثم افتح SolidCraft من جديد.",
|
||||
"update.download": "تنزيل",
|
||||
"update.download_open": "تنزيل وفتح",
|
||||
"update.downloading": "جارٍ التنزيل والتحقق… {done} من {total} MB",
|
||||
|
||||
@@ -3898,10 +3898,7 @@
|
||||
"the two origins are on the same component": "oba ishodišta su na istoj komponenti",
|
||||
"thread.automatic_size": "Automatska veličina",
|
||||
"thread.designation": "Oznaka",
|
||||
"thread.direction": "Smjer navoja",
|
||||
"thread.iso_metric": "ISO metrički",
|
||||
"thread.left_hand": "Lijevi",
|
||||
"thread.right_hand": "Desni",
|
||||
"thread.select_size": "Izaberite veličinu",
|
||||
"timeline.delete_after_marker_one": "Izbriši 1 operaciju nakon oznake vraćanja",
|
||||
"timeline.delete_after_marker_other": "Izbriši operacije nakon oznake vraćanja: {count}",
|
||||
|
||||
+84
-4
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "Obdélníkový",
|
||||
"Rectangular Tube": "Obdélníková trubka",
|
||||
"Reference face": "Referenční plocha",
|
||||
"Reference plane": "Referenční rovina",
|
||||
"Relief Depth": "Hloubka odlehčení",
|
||||
"Relief Width": "Šířka odlehčení",
|
||||
"Remove Tools": "Odstranit nástroje",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "Řez",
|
||||
"Section (optional)": "Průřez (volitelné)",
|
||||
"Section position": "Poloha průřezu",
|
||||
"Sections": "Průřezy",
|
||||
"Sections (one sketch each)": "Průřezy (každý jeden náčrt)",
|
||||
"Select Other": "Vybrat jiné",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Vybrat jiné: podržte levé tlačítko na modelu (nebo pravé tlačítko › Vybrat jiné…) a zobrazí se vše pod kurzorem; dalším kliknutím na stejné místo vyberete další prvek. Dvojklikem prostředním tlačítkem přejdete na výchozí pohled (Předvolby › Navigace).",
|
||||
@@ -859,6 +861,7 @@
|
||||
"cmd.component_extract": "Extrahovat do souboru",
|
||||
"cmd.component_finish_edit": "Dokončit úpravu",
|
||||
"cmd.component_from_bodies": "Vytvořit komponenty z těles",
|
||||
"cmd.component_hide_history": "Skrýt historii komponenty",
|
||||
"cmd.component_insert_design": "Vložit návrh",
|
||||
"cmd.component_links": "Seznam odkazů",
|
||||
"cmd.component_list": "Seznam komponent",
|
||||
@@ -875,6 +878,7 @@
|
||||
"cmd.component_rename": "Přejmenovat komponentu",
|
||||
"cmd.component_replace_with_link": "Nahradit odkazem",
|
||||
"cmd.component_revert_position": "Vrátit polohu",
|
||||
"cmd.component_show_history": "Zobrazit historii komponenty",
|
||||
"cmd.component_unlock": "Odemknout obsah",
|
||||
"cmd.component_update_links": "Aktualizovat odkazy",
|
||||
"cmd.config_activate": "Aktivovat konfiguraci",
|
||||
@@ -885,6 +889,7 @@
|
||||
"cmd.config_rule": "Pravidlo konfigurace",
|
||||
"cmd.config_rules": "Pravidla konfigurace",
|
||||
"cmd.config_table": "Zobrazit tabulku konfigurací",
|
||||
"cmd.construct.plane.axis_point": "Rovina osou a bodem",
|
||||
"cmd.construct_axis_edge": "Osa hranou",
|
||||
"cmd.construct_axis_normal_to_face": "Osa kolmá k ploše",
|
||||
"cmd.construct_axis_through_cylinder": "Osa válce/kužele/anuloidu",
|
||||
@@ -933,6 +938,11 @@
|
||||
"cmd.explode_show": "Zobrazit rozložený pohled",
|
||||
"cmd.expr_evaluate": "Vyhodnotit výraz",
|
||||
"cmd.face_delete": "Smazat plochu",
|
||||
"cmd.file.export_3mf": "Exportovat 3MF",
|
||||
"cmd.file.export_obj": "Exportovat OBJ",
|
||||
"cmd.file.export_step": "Exportovat STEP",
|
||||
"cmd.file.export_stl": "Exportovat STL",
|
||||
"cmd.file.export_urdf": "Exportovat URDF",
|
||||
"cmd.file_export": "Exportovat",
|
||||
"cmd.file_insert_mesh": "Vložit síť",
|
||||
"cmd.file_insert_step": "Vložit STEP",
|
||||
@@ -950,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "Zapnout barvy komponent",
|
||||
"cmd.inspect_colored_components_disable": "Vypnout barvy komponent",
|
||||
"cmd.inspect_compare": "Porovnat tělesa",
|
||||
"cmd.inspect_compare_picture": "Porovnat obrázek",
|
||||
"cmd.inspect_continuity": "Analýza spojitosti",
|
||||
"cmd.inspect_curvature_comb": "Analýza hřebenu křivosti",
|
||||
"cmd.inspect_curvature_map": "Mapa křivosti",
|
||||
@@ -1040,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "Upravit mračno bodů",
|
||||
"cmd.pointcloud_insert": "Vložit mračno bodů",
|
||||
"cmd.pointcloud_list": "Mračna bodů",
|
||||
"cmd.render.compare.divider": "Posunout dělicí čáru porovnání renderů",
|
||||
"cmd.render.result.discard": "Zahodit výsledek renderu",
|
||||
"cmd.render.result.select": "Vybrat výsledek renderu",
|
||||
"cmd.render_aging_describe": "Vrstvené stárnutí povrchu",
|
||||
"cmd.render_backends": "Renderovací jádra",
|
||||
"cmd.render_batch": "Renderovat všechny záběry",
|
||||
@@ -1254,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "Přesunout značku časové osy",
|
||||
"cmd.timeline_stop": "Zastavit historii",
|
||||
"cmd.timeline_suppress": "Potlačit prvek",
|
||||
"cmd.ui.assistant": "Zobrazit nebo skrýt Asistenta",
|
||||
"cmd.view.render_browser": "Prohlížeč renderu",
|
||||
"cmd.view.render_dock": "Přesunout panel renderu",
|
||||
"cmd.view.render_focus": "Soustředěný pohled na render",
|
||||
@@ -1288,9 +1303,11 @@
|
||||
"cmd.view_tool": "Navigační nástroj",
|
||||
"cmd.view_top": "Pohled shora",
|
||||
"cmd.view_visual_style": "Vizuální styl",
|
||||
"color.blue": "Modrá",
|
||||
"color.copy": "Kliknutím zkopírujete hodnoty barvy",
|
||||
"color.edit": "Kliknutím upravíte barvu",
|
||||
"color.gamma": "Hodnoty barvy v gama prostoru 0–255",
|
||||
"color.green": "Zelená",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "Zadejte #rrggbb nebo #rrggbbaa; šest číslic zachová neprůhlednost",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1298,6 +1315,7 @@
|
||||
"color.linear": "Hodnoty barvy v lineárním prostoru 0–1",
|
||||
"color.recent": "Nedávné barvy",
|
||||
"color.recent_help": "Barvy, které vyberete v libovolném poli barvy, se zobrazí zde",
|
||||
"color.red": "Červená",
|
||||
"color.saturation": "Sytost (v procentech)",
|
||||
"color.saturation_value": "Sytost a jas",
|
||||
"color.selected": "Vybraná barva",
|
||||
@@ -1305,6 +1323,8 @@
|
||||
"color.step_up": "Zvýšit o krok",
|
||||
"color.value": "Hodnota, tedy jas (v procentech)",
|
||||
"config.column_filter_empty": "Žádné odpovídající sloupce",
|
||||
"config.rule_parameter_columns": "Uvedeny jsou jen parametry, které mají v tabulce sloupec. Sloupec přidáte pomocí Přidat sloupec (+), abyste parametr mohli použít zde.",
|
||||
"config.rule_suppression_columns": "Uvedeny jsou jen prvky, které mají v tabulce sloupec Potlačit. Sloupec přidáte pomocí Přidat sloupec (+), abyste mohli prvek řídit zde.",
|
||||
"contact.components_count": "Počet komponent: {count}",
|
||||
"contact.enabled_label": "povoleno",
|
||||
"coupling.gear_1": "Kolo 1",
|
||||
@@ -1353,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "Vyberte tělesa nebo prvky: náčrt není ani jedno. Opakujte jeho křivky vlastním nástrojem Pole nebo Zrcadlit v náčrtu, nebo jej nejprve vysuňte.",
|
||||
"dialog.select": "↖ Vybrat",
|
||||
"dialog.selected_count": "↖ Vybráno: {count}",
|
||||
"dialog.selection_constraint": "Vazba {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · křivka {id}",
|
||||
"dialog.selection_guidance": "Vstup změníte jeho výběrem. Kliknutím na vybraný objekt jej odeberete; Ctrl/Cmd+klik výběr také přepíná. Zkontrolujte náhled a stiskněte OK.",
|
||||
"dialog.selection_midpoint": "Střed · {id}",
|
||||
"dialog.selection_profile": "{sketch} · profil {number}",
|
||||
"dialog.selection_step": "{step} z {total}: Vyberte {input}",
|
||||
"dialog.step": "Krok",
|
||||
"document.changed_on_disk": "Soubor {name} byl změněn na disku",
|
||||
@@ -1436,6 +1460,7 @@
|
||||
"help.construct.axis.two_points": "Vytvoří konstrukční osu procházející dvěma body, vrcholy nebo body náčrtu. Použijte ji pro osu vedoucí mezi dvěma známými polohami.",
|
||||
"help.construct.plane.along_path": "Vytvoří konstrukční rovinu kolmou k hraně nebo křivce v zadané poloze podél ní. Použijte to k náčrtu profilu pro tažení nebo příčného řezu podél cesty.",
|
||||
"help.construct.plane.at_angle": "Vytvoří konstrukční rovinu natočenou o úhel od roviny nebo plochy kolem osy nebo hrany. Použijte ji pro šikmé náčrty, například u úhlového držáku.",
|
||||
"help.construct.plane.axis_point": "Vytvoří konstrukční rovinu obsahující osu a bod mimo tuto osu. Vyberte osu počátku nebo konstrukční osu, přímou hranu nebo úsečku náčrtu a pak vrchol, bod náčrtu nebo konstrukční bod. Bod ležící na ose nemůže určit jednoznačnou rovinu.",
|
||||
"help.construct.plane.midplane": "Vytvoří konstrukční rovinu uprostřed mezi dvěma rovinami nebo rovinnými plochami, nebo rovinu půlící jejich úhel, když se protínají pod úhlem. Použijte ji jako rovinu souměrnosti pro zrcadlení.",
|
||||
"help.construct.plane.offset": "Vytvoří konstrukční rovinu rovnoběžnou s rovinou nebo rovinnou plochou ve zvolené vzdálenosti nebo procházející bodem. Použijte ji k náčrtu v dané výšce nad plochou.",
|
||||
"help.construct.plane.perpendicular": "Vytvoří konstrukční rovinu, která obsahuje přímku nebo hranu a je kolmá k rovině nebo ploše. Použijte ji k náčrtu kolmo na model podél hrany.",
|
||||
@@ -1812,6 +1837,8 @@
|
||||
"joint_move.blocked_hint": "Zkontrolujte typ kloubu (např. Pevný), ukotvení, limity kloubu a hodnoty řízené parametry.",
|
||||
"joint_move.blocked_title": "Tento díl nelze posunout",
|
||||
"joint_move.coarse": "Krok otáčení blízko osy (°)",
|
||||
"joint_move.conflict": "Kloub tohoto dílu je v konfliktu s ostatními: {joint}.",
|
||||
"joint_move.conflict_hint": "Upravte, potlačte nebo smažte kloub v konfliktu, aby mohly díl umístit ostatní klouby.",
|
||||
"joint_move.distance": "Vzdálenost od začátku",
|
||||
"joint_move.done": "Hotovo",
|
||||
"joint_move.fine": "Krok otáčení daleko od osy (°)",
|
||||
@@ -1911,6 +1938,8 @@
|
||||
"motion.track_name": "Stopa {number}",
|
||||
"nav.alias.help": "Shift+Alt+tažení levým tlačítkem otáčí, Shift+Alt+tažení prostředním posouvá, Shift+Alt+tažení pravým přibližuje; tažení prostředním tlačítkem také posouvá a pravým otáčí",
|
||||
"nav.blender.help": "Tažení prostředním tlačítkem otáčí, Shift+prostřední posouvá, Ctrl+prostřední přibližuje; tažení pravým tlačítkem kameru nemění",
|
||||
"nav.creo.help": "Tažení prostředním tlačítkem otáčí, Shift+prostřední posouvá, Ctrl+prostřední se svislým tažením přibližuje (tažením dolů přiblížíte); přibližuje i kolečko",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "Myš a 3D myš otáčejí přes oba póly a zachovávají natočení. Přichytávání os klávesou Shift je vypnuto.",
|
||||
"nav.free_orbit.label": "Volné otáčení",
|
||||
"nav.free_orbit.snap_help": "Myš a 3D myš otáčejí přes oba póly a zachovávají natočení. Podržením klávesy Shift při otáčení tažením přichytíte směr pohledu a natočení.",
|
||||
@@ -2063,12 +2092,19 @@
|
||||
"parts.family.button_head_cap_screw": "Šroub s půlkulovou hlavou a vnitřním šestihranem",
|
||||
"parts.family.dowel_pin": "Válcový kolík (lícovaný)",
|
||||
"parts.family.hex_bolt": "Šroub se šestihrannou hlavou",
|
||||
"parts.family.hex_cap_screw_inch": "Šroub se šestihrannou hlavou (palcový)",
|
||||
"parts.family.hex_nut": "Šestihranná matice",
|
||||
"parts.family.hex_nut_inch": "Šestihranná matice (palcová)",
|
||||
"parts.family.set_screw": "Stavěcí šroub s vnitřním šestihranem, s plochým koncem",
|
||||
"parts.family.socket_head_cap_screw": "Šroub s válcovou hlavou a vnitřním šestihranem",
|
||||
"parts.family.washer": "Plochá podložka",
|
||||
"parts.family.washer_sae": "Plochá podložka, SAE (úzká)",
|
||||
"parts.family.washer_uss": "Plochá podložka, USS (široká)",
|
||||
"parts.holes": "Otvory (volitelné)",
|
||||
"parts.placement_hint": "vyberte jeden nebo více otvorů, aby se do každého usadil díl, nebo OK umístí díl do počátku",
|
||||
"parts.system": "Soustava jednotek",
|
||||
"parts.system.inch": "Palcová",
|
||||
"parts.system.metric": "Metrická",
|
||||
"pattern.alignment_2": "Zarovnání 2",
|
||||
"pattern.alignment_hint": "Počty zahrnují originál. Na střed rozloží kopie rovnoměrně; Převrátit volí stranu navíc u sudých počtů.",
|
||||
"pattern.direction_1": "Směr 1",
|
||||
@@ -2130,6 +2166,8 @@
|
||||
"prefs.button_left": "Tažení levým tlačítkem",
|
||||
"prefs.button_middle": "Tažení prostředním tlačítkem",
|
||||
"prefs.button_right": "Tažení pravým tlačítkem",
|
||||
"prefs.carousel_next": "Další",
|
||||
"prefs.carousel_previous": "Předchozí",
|
||||
"prefs.choose_font": "Vybrat soubor písma…",
|
||||
"prefs.click": "Klik",
|
||||
"prefs.contact_shading": "Měkké stínování kontaktů",
|
||||
@@ -2142,6 +2180,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Zakřivené plochy",
|
||||
"prefs.curves": "Zakřivené plochy",
|
||||
"prefs.cut_preview_tint": "Zobrazit odebraný materiál červeně",
|
||||
"prefs.cut_preview_tint_help": "Náhled vyplní materiál, který prvek odebírá, i tvar, který nahrazuje, červeným průhledným stínem. Když je vypnuto, jsou tyto výplně zcela průhledné a kreslí se jen jejich obrysy.",
|
||||
"prefs.dark_parts_shading": "Zachovat tvar tmavých dílů",
|
||||
"prefs.dark_parts_shading_help": "Stínuje pohled v lineárním světle s jemným světlem oblohy, takže tmavé plochy zachovají přechod a nezčernají",
|
||||
"prefs.dead_zone_note": "Šrafované pásmo je mrtvá zóna: pohyb uvnitř něj se ignoruje.",
|
||||
@@ -2200,6 +2240,8 @@
|
||||
"prefs.interface_language": "Jazyk rozhraní",
|
||||
"prefs.interface_note": "Vzhled aplikace SolidCraft a rozmístění jejích částí.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_curved_roll_arrows": "Obrátit zakřivené šipky natočení",
|
||||
"prefs.invert_curved_roll_arrows_note": "Dvě zakřivené šipky v rohu krychle natáčejí pohled v rovině obrazovky. Zapnuto: šipka po směru hodinových ručiček natáčí pohled proti směru hodinových ručiček a šipka proti směru hodinových ručiček ho natáčí po směru. Vypnuto: každá šipka natáčí pohled tak, jak je zakřivená.",
|
||||
"prefs.invert_navigator_arrows": "Obrátit šipky otáčení krychle pohledu",
|
||||
"prefs.invert_navigator_arrows_note": "Šipky kolem krychle otáčejí kameru tím směrem, kam míří, jako ve Fusion a Inventor: šipka vlevo míří doprava a stáčí kameru doprava. Vypnuto: šipka otáčí krychli na stranu, na které leží, takže šipka vlevo stáčí kameru doleva. Zakřivené šipky natočení tím nejsou dotčeny.",
|
||||
"prefs.keep_work_on_tab_switch": "Ponechat dialogy a nástroje otevřené při přepínání návrhů",
|
||||
@@ -2259,6 +2301,8 @@
|
||||
"prefs.placement": "Umístění",
|
||||
"prefs.prefer_free_sketch_points": "Při překrytí bodů upřednostnit pohyblivé body náčrtu",
|
||||
"prefs.preview_choices": "Náhled vašich voleb",
|
||||
"prefs.preview_tint_new_faces": "Podbarvit nový povrch v náhledu",
|
||||
"prefs.preview_tint_new_faces_help": "Náhled prvku, který mění těleso, může vykreslit povrch, který prvek přidává, barvou náhledu. Když je vypnuto, těleso se vykreslí samo o sobě.",
|
||||
"prefs.print_face_highlight": "Zvýraznění tiskové plochy",
|
||||
"prefs.print_face_highlight_tip": "Jak výrazně se tisková plocha tělesa v pohledu obarví a obtáhne. 0 % nezobrazí žádné zvýraznění.",
|
||||
"prefs.profile_active": "Aktivní profil",
|
||||
@@ -2556,6 +2600,10 @@
|
||||
"render.base_color": "Základní barva · sRGB",
|
||||
"render.base_metallic": "Kovový podíl",
|
||||
"render.base_opaque_only": "Tento editor zachovává sémantiku neprůhledných těles. Pro optický průchod světla použijte samostatné ovládací prvky skla.",
|
||||
"render.batch_all_shots": "Všechny záběry ({count})",
|
||||
"render.batch_empty": "Před renderováním všech záběrů nejprve uložte záběr.",
|
||||
"render.batch_limit": "Renderování všech záběrů je omezeno na 32 záběrů.",
|
||||
"render.batch_status": "Probíhá: {running} · Čeká: {waiting}",
|
||||
"render.bloom_enable": "Záře ve výstupu",
|
||||
"render.bloom_help": "Kreativní záře z lineárních jasů se třemi normalizovanými průchody. Poloměr v pixelech výstupu, přičtení před sRGB. Nejde o simulaci čočky, objemu ani zlepšení konvergence. Surové EXR se nemění.",
|
||||
"render.bloom_opaque": "Záře vyžaduje neprůhledný výstup. Průhledné PNG se odmítne, neprovede se skryté sloučení.",
|
||||
@@ -2666,6 +2714,13 @@
|
||||
"render.choose_look": "Nejprve vyberte styl ve Vzhledech",
|
||||
"render.chromatic_offset": "Chromatický posun hran",
|
||||
"render.clear_stage": "Odebrat uloženou scénografii",
|
||||
"render.compare_divider": "Dělicí čára porovnání",
|
||||
"render.compare_live": "Živě",
|
||||
"render.compare_live_preview": "Živý náhled",
|
||||
"render.compare_render": "Render {number}",
|
||||
"render.compare_side": "Render {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} s · {renderer} · zachyceno ve revizi {revision}",
|
||||
"render.compare_swap": "Prohodit",
|
||||
"render.copy_request": "Kopírovat JSON",
|
||||
"render.cpu_aa_help": "Prostorové vzorky: více vzorků zlepší hrany a prodlouží vykreslení.",
|
||||
"render.cpu_area_add": "Přidat obdélníkové světlo CPU",
|
||||
@@ -3415,6 +3470,10 @@
|
||||
"render.result_retained": "Zobrazuje se poslední hotový obrázek; novější vykreslení jej zatím nenahradilo.",
|
||||
"render.result_sequence": "Kontrolní bod",
|
||||
"render.result_title": "Výsledek vykreslení",
|
||||
"render.results_choose": "Ponechané výsledky",
|
||||
"render.results_label": "Render {number} · {shot} · {seconds} s",
|
||||
"render.results_stale_label": "{result} [zastaralý]",
|
||||
"render.results_unnamed": "Obrázek",
|
||||
"render.return_design": "Zpět k návrhu",
|
||||
"render.review_stale": "Tato kontrola je zastaralá. Před použitím ji vyřešte nebo proveďte znovu.",
|
||||
"render.ribbon_aging": "Stárnutí",
|
||||
@@ -3898,10 +3957,7 @@
|
||||
"the two origins are on the same component": "oba počátky jsou na téže komponentě",
|
||||
"thread.automatic_size": "Automatická velikost",
|
||||
"thread.designation": "Označení",
|
||||
"thread.direction": "Směr závitu",
|
||||
"thread.iso_metric": "Metrický ISO",
|
||||
"thread.left_hand": "Levotočivý",
|
||||
"thread.right_hand": "Pravotočivý",
|
||||
"thread.select_size": "Vybrat velikost",
|
||||
"timeline.delete_after_marker_one": "Smazat 1 prvek za značkou historie",
|
||||
"timeline.delete_after_marker_other": "Smazat prvky za značkou historie: {count}",
|
||||
@@ -4075,6 +4131,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Spusťte toto v terminálu a pak znovu stiskněte Připojit:",
|
||||
"ui.agent_chat_auth_title": "Přihlásit se k agentovi",
|
||||
"ui.agent_chat_cancel": "Zrušit",
|
||||
"ui.agent_chat_cancel_timeout": "Agent na Zastavit neodpověděl a byl odpojen. Pro pokračování se připojte znovu.",
|
||||
"ui.agent_chat_cannot_start": "Nainstalováno, ale nespouští se: {error}",
|
||||
"ui.agent_chat_changes": "Změny: {n}, jeden krok zpět",
|
||||
"ui.agent_chat_changes_n": "Změn: {n}",
|
||||
@@ -4084,11 +4141,13 @@
|
||||
"ui.agent_chat_command": "Příkaz agenta",
|
||||
"ui.agent_chat_connect": "Připojit",
|
||||
"ui.agent_chat_copy": "Kopírovat",
|
||||
"ui.agent_chat_copy_message": "Kopírovat zprávu",
|
||||
"ui.agent_chat_ctx_bodies": "Tělesa",
|
||||
"ui.agent_chat_ctx_elements": "Elementy",
|
||||
"ui.agent_chat_ctx_features": "Prvky",
|
||||
"ui.agent_chat_ctx_more": "+{n} dalších",
|
||||
"ui.agent_chat_ctx_other": "Jiné",
|
||||
"ui.agent_chat_custom_heading": "Vlastní model",
|
||||
"ui.agent_chat_custom_hint": "Jakýkoli program, který komunikuje protokolem Agent Client Protocol přes stdin a stdout. Nespouští se přes shell.",
|
||||
"ui.agent_chat_denied": "Zamítnuto",
|
||||
"ui.agent_chat_deny": "Zamítnout",
|
||||
@@ -4132,8 +4191,16 @@
|
||||
"ui.agent_chat_mode_all": "Vše",
|
||||
"ui.agent_chat_mode_ask": "Ptát se",
|
||||
"ui.agent_chat_mode_edits": "Úpravy",
|
||||
"ui.agent_chat_model_custom": "Použít vlastní: {model}",
|
||||
"ui.agent_chat_model_no_tools": "{model} nemůže používat nástroje SolidCraftu, takže umí jen chatovat. Vyberte model s podporou nástrojů.",
|
||||
"ui.agent_chat_model_rejected": "Agent nepřijal model {model}: {error}",
|
||||
"ui.agent_chat_model_search": "Hledat modely…",
|
||||
"ui.agent_chat_model_tip": "Model, který agent používá. Vaše volba se pro tohoto agenta zapamatuje.",
|
||||
"ui.agent_chat_models_failed": "Seznam modelů se nepodařilo načíst: {error}",
|
||||
"ui.agent_chat_models_heading": "Dostupné modely",
|
||||
"ui.agent_chat_models_listing": "Načítání seznamu modelů…",
|
||||
"ui.agent_chat_models_none": "Endpoint neuvádí žádné modely",
|
||||
"ui.agent_chat_models_not_a_list": "Odpověď nebyla seznam modelů",
|
||||
"ui.agent_chat_new": "Nový chat",
|
||||
"ui.agent_chat_new_confirm": "Začít nový chat? Aktuální bude vymazán.",
|
||||
"ui.agent_chat_next_picture": "Další obrázek",
|
||||
@@ -4407,14 +4474,21 @@
|
||||
"ui.construction.contact_hint": "Tečna: vyberte kružnici/oblouk nebo otevřený splajn/kuželosečku, pak bod dotyku (volné křivky: koncové body)",
|
||||
"ui.construction.external_hint": "Tečna: vyberte kružnici nebo oblouk, vnější bod, pak požadované řešení",
|
||||
"ui.construction.free_hint": "Konstrukční přímka: klikněte na dva body; pravým tlačítkem začnete znovu",
|
||||
"ui.construction.normal_needs_length": "Nekonečná přímka je vždy konstrukční: zadejte délku, abyste nakreslili běžnou úsečku",
|
||||
"ui.construction.point_hint": "Konstrukční přímka: klikněte na bod, kterým má procházet",
|
||||
"ui.construction.reference_hint": "Konstrukční přímka: vyberte přímku, pak bod; pravým tlačítkem změníte referenci",
|
||||
"ui.construction.ribbon_label": "Pom. přímky",
|
||||
"ui.construction.side_hint": "Konstrukční přímka: klikněte na stranu, kterým směrem přímka vede; pravým tlačítkem začnete znovu",
|
||||
"ui.context_menu": "Místní nabídka",
|
||||
"ui.continuity_crease": "Hrana",
|
||||
"ui.continuity_curvature": "Křivost",
|
||||
"ui.continuity_curvature_change": "Změna křivosti",
|
||||
"ui.continuity_g0": "G0 hrana",
|
||||
"ui.continuity_g1": "G1 tečně",
|
||||
"ui.continuity_g2": "G2 křivost",
|
||||
"ui.continuity_gap": "Mezera",
|
||||
"ui.continuity_position": "Poloha",
|
||||
"ui.continuity_tangent": "Tečnost",
|
||||
"ui.copy": "Kopírovat",
|
||||
"ui.corner": "Roh",
|
||||
"ui.corners": "Rohy",
|
||||
@@ -4675,6 +4749,7 @@
|
||||
"ui.hideOrigin": "Skrýt počátek",
|
||||
"ui.hidePlanes": "Skrýt roviny",
|
||||
"ui.hideProfile": "Skrýt profil",
|
||||
"ui.hide_component_history": "Skrýt její historii",
|
||||
"ui.hide_origin_by_default": "Ve výchozím stavu skrýt počátek",
|
||||
"ui.hide_origin_note": "Nové návrhy začínají se skrytou složkou Počátek. Návrh si ponechá vlastní nastavení počátku, jakmile ho zobrazíte nebo skryjete.",
|
||||
"ui.history_back": "O krok zpět",
|
||||
@@ -4682,6 +4757,7 @@
|
||||
"ui.history_edge_hint": "dvakrát klikněte na hranu",
|
||||
"ui.history_first": "Přejít na začátek",
|
||||
"ui.history_forward": "O krok vpřed",
|
||||
"ui.history_hide_hidden": "Skrýt skryté komponenty",
|
||||
"ui.history_hide_names": "Skrýt názvy",
|
||||
"ui.history_last": "Přejít na konec",
|
||||
"ui.history_marker_tip": "Značka historie: přetažením vrátíte model zpět; nové prvky se vkládají sem",
|
||||
@@ -4691,10 +4767,12 @@
|
||||
"ui.history_play_button_show": "Zobrazit tlačítko Přehrát",
|
||||
"ui.history_play_finish_action": "Před přehráním historie dokončete aktuální akci",
|
||||
"ui.history_play_roll_back": "Před přehráním posuňte značku historie zpět",
|
||||
"ui.history_show_hidden": "Zobrazit skryté komponenty",
|
||||
"ui.history_show_names": "Zobrazit názvy",
|
||||
"ui.history_sketch_listed_under": "Uvedeno pod prvkem {feature}",
|
||||
"ui.history_title": "Historie",
|
||||
"ui.history_toggle_names": "Zobrazit nebo skrýt názvy prvků",
|
||||
"ui.history_waits_for_editor": "Zpět, Znovu a Historie počkají, dokud se neotevře editor prvku",
|
||||
"ui.hole": "Otvor",
|
||||
"ui.hole_tap_type": "Typ závitu otvoru",
|
||||
"ui.hole_type": "Typ otvoru",
|
||||
@@ -4975,6 +5053,7 @@
|
||||
"ui.showDimension": "Zobrazit kótu",
|
||||
"ui.showOrigin": "Zobrazit počátek",
|
||||
"ui.showPlanes": "Zobrazit roviny",
|
||||
"ui.show_component_history": "Zobrazit její historii",
|
||||
"ui.show_constraints": "Vazby",
|
||||
"ui.show_construction_geometries": "Konstrukční geometrie",
|
||||
"ui.show_dimensions": "Kóty",
|
||||
@@ -5157,7 +5236,7 @@
|
||||
"update.check_unavailable": "Pro tuto instalaci není kontrola aktualizací dostupná.",
|
||||
"update.checking": "Kontrola…",
|
||||
"update.copy": "Kopírovat",
|
||||
"update.dmg_opened": "Obraz disku je otevřený a ověřený: přetáhněte SolidCraft do Aplikací místo staré kopie a poté SolidCraft restartujte.",
|
||||
"update.dmg_opened": "Obraz disku je otevřený a ověřený: ukončete SolidCraft, přetáhněte nový SolidCraft do Aplikací místo staré kopie a poté SolidCraft znovu otevřete.",
|
||||
"update.download": "Stáhnout",
|
||||
"update.download_open": "Stáhnout a otevřít",
|
||||
"update.downloading": "Stahování a ověřování… {done} z {total} MB",
|
||||
@@ -5201,6 +5280,7 @@
|
||||
"web.open_link": "Otevřít propojený návrh?",
|
||||
"web.open_link_confirm": "Existují neuložené změny. Otevřít propojený návrh na nové kartě?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ vybrat · Enter / Tab doplnit · Esc zavřít",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ výběr · Enter použije tuto hodnotu",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ výběr · Enter použije, co jste napsali · Esc zavře",
|
||||
"⌘Enter continues on GitHub.": "⌘Enter pokračuje na GitHubu."
|
||||
}
|
||||
|
||||
+84
-4
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "Rektangulær",
|
||||
"Rectangular Tube": "Rektangulært hulprofil",
|
||||
"Reference face": "Referenceflade",
|
||||
"Reference plane": "Referenceplan",
|
||||
"Relief Depth": "Aflastningsdybde",
|
||||
"Relief Width": "Aflastningsbredde",
|
||||
"Remove Tools": "Fjern værktøjsemner",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "Snit",
|
||||
"Section (optional)": "Tværsnit (valgfrit)",
|
||||
"Section position": "Profilets placering",
|
||||
"Sections": "Tværsnit",
|
||||
"Sections (one sketch each)": "Tværsnit (én skitse til hvert)",
|
||||
"Select Other": "Vælg andet",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Vælg andet: Hold venstre museknap nede uden at flytte markøren på modellen (eller højreklik › Vælg andet…) for at vise alt under markøren. Klik igen samme sted for at vælge næste objekt. Dobbeltklik på den midterste museknap for at gå til Hjem (Indstillinger › Navigation).",
|
||||
@@ -859,6 +861,7 @@
|
||||
"cmd.component_extract": "Udtræk til fil",
|
||||
"cmd.component_finish_edit": "Afslut redigering",
|
||||
"cmd.component_from_bodies": "Opret komponenter fra emner",
|
||||
"cmd.component_hide_history": "Skjul komponenthistorik",
|
||||
"cmd.component_insert_design": "Indsæt design",
|
||||
"cmd.component_links": "Vis forbindelser",
|
||||
"cmd.component_list": "Vis komponenter",
|
||||
@@ -875,6 +878,7 @@
|
||||
"cmd.component_rename": "Omdøb komponent",
|
||||
"cmd.component_replace_with_link": "Erstat med link",
|
||||
"cmd.component_revert_position": "Tilbagefør position",
|
||||
"cmd.component_show_history": "Vis komponenthistorik",
|
||||
"cmd.component_unlock": "Lås indhold op",
|
||||
"cmd.component_update_links": "Opdatér forbindelser",
|
||||
"cmd.config_activate": "Aktivér konfiguration",
|
||||
@@ -885,6 +889,7 @@
|
||||
"cmd.config_rule": "Konfigurationsregel",
|
||||
"cmd.config_rules": "Konfigurationsregler",
|
||||
"cmd.config_table": "Vis konfigurationstabel",
|
||||
"cmd.construct.plane.axis_point": "Plan gennem akse og punkt",
|
||||
"cmd.construct_axis_edge": "Akse gennem kant",
|
||||
"cmd.construct_axis_normal_to_face": "Akse vinkelret på flade",
|
||||
"cmd.construct_axis_through_cylinder": "Akse gennem cylinder/kegle/torus",
|
||||
@@ -933,6 +938,11 @@
|
||||
"cmd.explode_show": "Vis eksploderet visning",
|
||||
"cmd.expr_evaluate": "Beregn udtryk",
|
||||
"cmd.face_delete": "Slet flade",
|
||||
"cmd.file.export_3mf": "Eksportér 3MF",
|
||||
"cmd.file.export_obj": "Eksportér OBJ",
|
||||
"cmd.file.export_step": "Eksportér STEP",
|
||||
"cmd.file.export_stl": "Eksportér STL",
|
||||
"cmd.file.export_urdf": "Eksportér URDF",
|
||||
"cmd.file_export": "Eksportér",
|
||||
"cmd.file_insert_mesh": "Indsæt mesh",
|
||||
"cmd.file_insert_step": "Indsæt STEP",
|
||||
@@ -950,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "Aktivér komponentfarver",
|
||||
"cmd.inspect_colored_components_disable": "Deaktivér komponentfarver",
|
||||
"cmd.inspect_compare": "Sammenlign emner",
|
||||
"cmd.inspect_compare_picture": "Sammenlign billede",
|
||||
"cmd.inspect_continuity": "Kontinuitetsanalyse",
|
||||
"cmd.inspect_curvature_comb": "Krumningskamanalyse",
|
||||
"cmd.inspect_curvature_map": "Krumningskortanalyse",
|
||||
@@ -1040,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "Redigér punktsky",
|
||||
"cmd.pointcloud_insert": "Indsæt punktsky",
|
||||
"cmd.pointcloud_list": "Punktskyer",
|
||||
"cmd.render.compare.divider": "Flyt skillelinjen i renderingssammenligningen",
|
||||
"cmd.render.result.discard": "Kassér renderingsresultat",
|
||||
"cmd.render.result.select": "Vælg renderingsresultat",
|
||||
"cmd.render_aging_describe": "Lagdelt overfladeældning",
|
||||
"cmd.render_backends": "Renderingsmotorer",
|
||||
"cmd.render_batch": "Render alle optagelser",
|
||||
@@ -1254,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "Flyt tilbagerulningsmarkør",
|
||||
"cmd.timeline_stop": "Stop historik",
|
||||
"cmd.timeline_suppress": "Undertryk feature",
|
||||
"cmd.ui.assistant": "Vis/skjul assistent",
|
||||
"cmd.view.render_browser": "Renderingsbrowser",
|
||||
"cmd.view.render_dock": "Flyt renderingsdock",
|
||||
"cmd.view.render_focus": "Fokusvisning af rendering",
|
||||
@@ -1288,9 +1303,11 @@
|
||||
"cmd.view_tool": "Navigationsværktøj",
|
||||
"cmd.view_top": "Visning ovenfra",
|
||||
"cmd.view_visual_style": "Visuel stil",
|
||||
"color.blue": "Blå",
|
||||
"color.copy": "Klik for at kopiere farveværdier",
|
||||
"color.edit": "Klik for at redigere farve",
|
||||
"color.gamma": "Viser farveværdier i 0-255 gammarum",
|
||||
"color.green": "Grøn",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "Angiv #rrggbb eller #rrggbbaa; seks cifre bevarer opaciteten",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1298,6 +1315,7 @@
|
||||
"color.linear": "Viser farveværdier i 0-1 lineært rum",
|
||||
"color.recent": "Seneste farver",
|
||||
"color.recent_help": "Farver, du vælger i et farvefelt, vises her",
|
||||
"color.red": "Rød",
|
||||
"color.saturation": "Mætning (procent)",
|
||||
"color.saturation_value": "Mætning og værdi",
|
||||
"color.selected": "Valgt farve",
|
||||
@@ -1305,6 +1323,8 @@
|
||||
"color.step_up": "Ét trin op",
|
||||
"color.value": "Værdi, lysstyrken (procent)",
|
||||
"config.column_filter_empty": "Ingen matchende kolonner",
|
||||
"config.rule_parameter_columns": "Kun parametre med en kolonne i tabellen vises. Tilføj en kolonne med Tilføj kolonne (+) for at bruge parameteren her.",
|
||||
"config.rule_suppression_columns": "Kun features med en Undertryk-kolonne i tabellen vises. Tilføj en kolonne med Tilføj kolonne (+) for at styre undertrykkelsen her.",
|
||||
"contact.components_count": "{count} komponenter",
|
||||
"contact.enabled_label": "aktiveret",
|
||||
"coupling.gear_1": "Tandhjul 1",
|
||||
@@ -1353,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "Vælg emner eller features: En skitse er ingen af delene. Gentag dens kurver med skitsens eget Mønster eller Spejling, eller ekstruder den først.",
|
||||
"dialog.select": "↖ Vælg",
|
||||
"dialog.selected_count": "↖ Valgte: {count}",
|
||||
"dialog.selection_constraint": "Binding {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · kurve {id}",
|
||||
"dialog.selection_guidance": "Vælg et input for at ændre det. Klik på valgte objekter for at fjerne dem; Ctrl/Cmd-klik skifter også deres valgstatus. Gennemgå forhåndsvisningen, og tryk derefter på OK.",
|
||||
"dialog.selection_midpoint": "Midtpunkt · {id}",
|
||||
"dialog.selection_profile": "{sketch} · profil {number}",
|
||||
"dialog.selection_step": "{step} af {total}: Vælg {input}",
|
||||
"dialog.step": "Trin",
|
||||
"document.changed_on_disk": "{name} er ændret på disken",
|
||||
@@ -1436,6 +1460,7 @@
|
||||
"help.construct.axis.two_points": "Opretter en konstruktionsakse gennem to punkter, hjørnepunkter eller skitsepunkter. Brug den til en akse mellem to kendte positioner.",
|
||||
"help.construct.plane.along_path": "Opretter et konstruktionsplan vinkelret på en kant eller kurve i en given position langs den. Brug det til at skitsere en profil til et sweep eller et snit af en bane.",
|
||||
"help.construct.plane.at_angle": "Opretter et konstruktionsplan, der er drejet med en vinkel fra et plan eller en flade omkring en akse eller kant. Brug det til skrå skitser, f.eks. et vinklet beslag.",
|
||||
"help.construct.plane.axis_point": "Opretter et konstruktionsplan, der indeholder en akse og et punkt uden for aksen. Vælg en akse fra nulpunktet eller en konstruktionsakse, en ret kant eller skitselinje og derefter et hjørnepunkt, skitsepunkt eller konstruktionspunkt. Et punkt på aksen kan ikke definere et entydigt plan.",
|
||||
"help.construct.plane.midplane": "Opretter et konstruktionsplan midt mellem to planer eller plane flader eller halverende dem, hvor de mødes i en vinkel. Brug det som symmetriplan til spejling.",
|
||||
"help.construct.plane.offset": "Opretter et konstruktionsplan parallelt med et plan eller en plan flade i en afstand eller gennem et punkt. Brug det til at skitsere i en bestemt højde over en flade.",
|
||||
"help.construct.plane.perpendicular": "Opretter et konstruktionsplan, der indeholder en linje eller kant og står vinkelret på et plan eller en flade. Brug det til at skitsere opretstående på en model langs en kant.",
|
||||
@@ -1812,6 +1837,8 @@
|
||||
"joint_move.blocked_hint": "Kontrollér ledtypen (f.eks. Stiv), fastgørelse, bevægelsesgrænser for led og parameterstyrede værdier.",
|
||||
"joint_move.blocked_title": "Kan ikke flytte dette emne",
|
||||
"joint_move.coarse": "Rotationstrin nær aksen (°)",
|
||||
"joint_move.conflict": "Et led på dette emne er i konflikt med de andre: {joint}.",
|
||||
"joint_move.conflict_hint": "Redigér, undertryk eller slet det modstridende led, så de andre led kan placere emnet.",
|
||||
"joint_move.distance": "Afstand fra start",
|
||||
"joint_move.done": "Færdig",
|
||||
"joint_move.fine": "Rotationstrin langt fra aksen (°)",
|
||||
@@ -1911,6 +1938,8 @@
|
||||
"motion.track_name": "Spor {number}",
|
||||
"nav.alias.help": "Shift+Alt+træk med venstre museknap roterer visningen, Shift+Alt+midterste panorerer, Shift+Alt+højre zoomer; træk med midterste museknap panorerer også, og træk med højre roterer visningen",
|
||||
"nav.blender.help": "Træk med midterste museknap roterer visningen, Shift+midterste panorerer, Ctrl+midterste zoomer; træk med højre museknap påvirker ikke kameraet",
|
||||
"nav.creo.help": "Træk med midterste museknap roterer visningen, Shift+midterste panorerer, Ctrl+træk lodret med midterste zoomer (ned for at zoome ind); rulning med hjulet zoomer",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "Musen og 3D-musen roterer gennem begge poler og bevarer rulning. Snap til akser med Shift er slået fra.",
|
||||
"nav.free_orbit.label": "Fri rotation af visningen",
|
||||
"nav.free_orbit.snap_help": "Musen og 3D-musen roterer gennem begge poler og bevarer rulning. Hold Shift nede under et rotationstræk for at snappe visningsretningen og rulningen.",
|
||||
@@ -2063,12 +2092,19 @@
|
||||
"parts.family.button_head_cap_screw": "Rundhovedet skrue med indvendig sekskant",
|
||||
"parts.family.dowel_pin": "Cylindrisk stift (dyvel)",
|
||||
"parts.family.hex_bolt": "Sekskanthovedet bolt / skrue",
|
||||
"parts.family.hex_cap_screw_inch": "Sekskantskrue (tommer)",
|
||||
"parts.family.hex_nut": "Sekskantmøtrik",
|
||||
"parts.family.hex_nut_inch": "Sekskantmøtrik (tommer)",
|
||||
"parts.family.set_screw": "Stilleskrue med indvendig sekskant, flad spids",
|
||||
"parts.family.socket_head_cap_screw": "Cylinderskrue med indvendig sekskant",
|
||||
"parts.family.washer": "Almindelig skive",
|
||||
"parts.family.washer_sae": "Almindelig skive, SAE (smal)",
|
||||
"parts.family.washer_uss": "Almindelig skive, USS (bred)",
|
||||
"parts.holes": "Huller (valgfrit)",
|
||||
"parts.placement_hint": "vælg et eller flere huller for at sætte en del i hvert, eller tryk OK for at placere den ved nulpunktet",
|
||||
"parts.system": "Enhedssystem",
|
||||
"parts.system.inch": "Tommer",
|
||||
"parts.system.metric": "Metrisk",
|
||||
"pattern.alignment_2": "Justering 2",
|
||||
"pattern.alignment_hint": "Antal omfatter originalen. Centrér fordeler kopierne ligeligt; Vend vælger siden med den ekstra kopi ved et lige samlet antal.",
|
||||
"pattern.direction_1": "Retning 1",
|
||||
@@ -2130,6 +2166,8 @@
|
||||
"prefs.button_left": "Træk med venstre museknap",
|
||||
"prefs.button_middle": "Træk med midterste museknap",
|
||||
"prefs.button_right": "Træk med højre museknap",
|
||||
"prefs.carousel_next": "Næste",
|
||||
"prefs.carousel_previous": "Forrige",
|
||||
"prefs.choose_font": "Vælg skrifttypefil…",
|
||||
"prefs.click": "Klik",
|
||||
"prefs.contact_shading": "Blød kontaktskygning",
|
||||
@@ -2142,6 +2180,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Krumme overflader",
|
||||
"prefs.curves": "Krumme overflader",
|
||||
"prefs.cut_preview_tint": "Vis fjernet materiale i rødt",
|
||||
"prefs.cut_preview_tint_help": "En forhåndsvisning udfylder det materiale, som en feature fjerner, og den form, den erstatter, med et rødt, gennemsigtigt spøgelse. Når indstillingen er slået fra, er udfyldningerne helt gennemsigtige, og kun deres omrids tegnes.",
|
||||
"prefs.dark_parts_shading": "Bevar formen på mørke dele",
|
||||
"prefs.dark_parts_shading_help": "Skygger visningen i lineært lys med en blød himmelfyld, så mørke flader beholder et farveforløb i stedet for at blive sorte",
|
||||
"prefs.dead_zone_note": "Det skraverede bånd er dødzonen: Bevægelser inden for det ignoreres.",
|
||||
@@ -2200,6 +2240,8 @@
|
||||
"prefs.interface_language": "Sprog i brugergrænsefladen",
|
||||
"prefs.interface_note": "SolidCrafts udseende og placeringen af dets dele.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_curved_roll_arrows": "Vend de buede rulningspile om",
|
||||
"prefs.invert_curved_roll_arrows_note": "De to buede pile i kubens hjørne ruller visningen i skærmplanet. Slået til: Pilen med uret ruller mod uret, og pilen mod uret ruller med uret. Slået fra: Hver pil ruller visningen den vej, den krummer.",
|
||||
"prefs.invert_navigator_arrows": "Vend visningskubens rotationspile om",
|
||||
"prefs.invert_navigator_arrows_note": "Pilene rundt om kuben roterer kameraet i den retning, de peger, som i Fusion og Inventor: Pilen til venstre peger mod højre og svinger kameraet til højre. Slået fra: En pil drejer kuben mod den side, den sidder på, så venstre pil svinger kameraet til venstre. De buede rulningspile påvirkes ikke.",
|
||||
"prefs.keep_work_on_tab_switch": "Behold dialoger og værktøjer åbne, når du skifter design",
|
||||
@@ -2259,6 +2301,8 @@
|
||||
"prefs.placement": "Placering",
|
||||
"prefs.prefer_free_sketch_points": "Foretræk flytbare skitsepunkter, når punkter overlapper",
|
||||
"prefs.preview_choices": "Forhåndsvisning af dine valg",
|
||||
"prefs.preview_tint_new_faces": "Farv ny overflade i en forhåndsvisning",
|
||||
"prefs.preview_tint_new_faces_help": "En forhåndsvisning af en feature, der ændrer et emne, kan tegne den overflade, den tilføjer, i forhåndsvisningens farve. Når indstillingen er slået fra, tegnes emnet, som det er.",
|
||||
"prefs.print_face_highlight": "Fremhævning af printflade",
|
||||
"prefs.print_face_highlight_tip": "Hvor kraftigt et legemes printflade farves og omrids i visningen. 0 % viser ingen fremhævning.",
|
||||
"prefs.profile_active": "Aktiv profil",
|
||||
@@ -2556,6 +2600,10 @@
|
||||
"render.base_color": "Basisfarve · sRGB",
|
||||
"render.base_metallic": "Metallisk andel",
|
||||
"render.base_opaque_only": "Denne editor bevarer emnets uigennemsigtighed. Brug de eksplicitte glasindstillinger til optisk transmission.",
|
||||
"render.batch_all_shots": "Alle optagelser ({count})",
|
||||
"render.batch_empty": "Gem en optagelse, før alle optagelser renderes.",
|
||||
"render.batch_limit": "Rendering af alle optagelser er begrænset til 32 optagelser.",
|
||||
"render.batch_status": "{running} kører · {waiting} venter",
|
||||
"render.bloom_enable": "Vis bloom",
|
||||
"render.bloom_help": "En kreativ glød, der kun påvirker output, ud fra positive lineære højlys og spredt med tre normaliserede box-pas. Radius er i outputpixels. Lægges til i lineært visningslys før sRGB; ikke en linseeffekt, en volumeneffekt eller en forbedring af konvergens. Den rå EXR er uændret.",
|
||||
"render.bloom_opaque": "Bloom kræver uigennemsigtigt output. Gennemsigtig PNG afvises og gøres ikke stiltiende uigennemsigtig.",
|
||||
@@ -2666,6 +2714,13 @@
|
||||
"render.choose_look": "Vælg først et udseende under Udseender",
|
||||
"render.chromatic_offset": "Kromatisk kantforskydning",
|
||||
"render.clear_stage": "Fjern gemt kulisse",
|
||||
"render.compare_divider": "Sammenligningens skillelinje",
|
||||
"render.compare_live": "Live",
|
||||
"render.compare_live_preview": "Live-forhåndsvisning",
|
||||
"render.compare_render": "Rendering {number}",
|
||||
"render.compare_side": "Rendering {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} s · {renderer} · registreret ved revision {revision}",
|
||||
"render.compare_swap": "Ombyt",
|
||||
"render.copy_request": "Kopiér JSON",
|
||||
"render.cpu_aa_help": "Rumlige samples: flere samples forbedrer kanter og øger renderingstiden.",
|
||||
"render.cpu_area_add": "Tilføj rektangulært CPU-lys",
|
||||
@@ -3415,6 +3470,10 @@
|
||||
"render.result_retained": "Viser det seneste færdige billede; en nyere rendering har ikke erstattet det.",
|
||||
"render.result_sequence": "Kontrolpunkt",
|
||||
"render.result_title": "Renderingsresultat",
|
||||
"render.results_choose": "Bevarede resultater",
|
||||
"render.results_label": "Rendering {number} · {shot} · {seconds} s",
|
||||
"render.results_stale_label": "{result} [forældet]",
|
||||
"render.results_unnamed": "Billede",
|
||||
"render.return_design": "Tilbage til Design",
|
||||
"render.review_stale": "Denne gennemgang er forældet. Afklar den, eller kontrollér igen, før du anvender den.",
|
||||
"render.ribbon_aging": "Ældning",
|
||||
@@ -3898,10 +3957,7 @@
|
||||
"the two origins are on the same component": "de to nulpunkter er på samme komponent",
|
||||
"thread.automatic_size": "Automatisk størrelse",
|
||||
"thread.designation": "Betegnelse",
|
||||
"thread.direction": "Gevindretning",
|
||||
"thread.iso_metric": "ISO-metrisk",
|
||||
"thread.left_hand": "Venstregænget",
|
||||
"thread.right_hand": "Højregænget",
|
||||
"thread.select_size": "Vælg størrelse",
|
||||
"timeline.delete_after_marker_one": "Slet 1 feature efter historikmarkøren",
|
||||
"timeline.delete_after_marker_other": "Slet {count} features efter historikmarkøren",
|
||||
@@ -4075,6 +4131,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Kør dette i en terminal, og tryk derefter på Forbind igen:",
|
||||
"ui.agent_chat_auth_title": "Log ind hos agenten",
|
||||
"ui.agent_chat_cancel": "Annullér",
|
||||
"ui.agent_chat_cancel_timeout": "Agenten svarede ikke på Stop og blev afbrudt. Forbind igen for at fortsætte.",
|
||||
"ui.agent_chat_cannot_start": "Installeret, men den starter ikke: {error}",
|
||||
"ui.agent_chat_changes": "Ændringer: {n}, ét fortrydelsestrin",
|
||||
"ui.agent_chat_changes_n": "{n} ændringer",
|
||||
@@ -4084,11 +4141,13 @@
|
||||
"ui.agent_chat_command": "Agentkommando",
|
||||
"ui.agent_chat_connect": "Forbind",
|
||||
"ui.agent_chat_copy": "Kopiér",
|
||||
"ui.agent_chat_copy_message": "Kopiér besked",
|
||||
"ui.agent_chat_ctx_bodies": "Emner",
|
||||
"ui.agent_chat_ctx_elements": "Elementer",
|
||||
"ui.agent_chat_ctx_features": "Features",
|
||||
"ui.agent_chat_ctx_more": "+{n} flere",
|
||||
"ui.agent_chat_ctx_other": "Andet",
|
||||
"ui.agent_chat_custom_heading": "Brugerdefineret model",
|
||||
"ui.agent_chat_custom_hint": "Ethvert program, der bruger Agent Client Protocol via stdin og stdout. Det køres uden en kommandoskal.",
|
||||
"ui.agent_chat_denied": "Afvist",
|
||||
"ui.agent_chat_deny": "Afvis",
|
||||
@@ -4132,8 +4191,16 @@
|
||||
"ui.agent_chat_mode_all": "Alt",
|
||||
"ui.agent_chat_mode_ask": "Spørg",
|
||||
"ui.agent_chat_mode_edits": "Ændringer",
|
||||
"ui.agent_chat_model_custom": "Brug brugerdefineret model: {model}",
|
||||
"ui.agent_chat_model_no_tools": "{model} kan ikke bruge SolidCrafts værktøjer og kan derfor kun chatte. Vælg en model med understøttelse af værktøjer.",
|
||||
"ui.agent_chat_model_rejected": "Agenten accepterede ikke {model}: {error}",
|
||||
"ui.agent_chat_model_search": "Søg efter modeller…",
|
||||
"ui.agent_chat_model_tip": "Modellen, som agenten bruger. Dit valg huskes for denne agent.",
|
||||
"ui.agent_chat_models_failed": "Kunne ikke hente listen over modeller: {error}",
|
||||
"ui.agent_chat_models_heading": "Tilgængelige modeller",
|
||||
"ui.agent_chat_models_listing": "Henter modeller…",
|
||||
"ui.agent_chat_models_none": "Endepunktet viser ingen modeller",
|
||||
"ui.agent_chat_models_not_a_list": "Svaret var ikke en liste over modeller",
|
||||
"ui.agent_chat_new": "Ny chat",
|
||||
"ui.agent_chat_new_confirm": "Start en ny chat? Den aktuelle chat ryddes.",
|
||||
"ui.agent_chat_next_picture": "Næste billede",
|
||||
@@ -4407,14 +4474,21 @@
|
||||
"ui.construction.contact_hint": "Tangent: Vælg en cirkel/bue eller en åben spline/keglesnit, og vælg derefter røringspunktet (frie kurver: endepunkter)",
|
||||
"ui.construction.external_hint": "Tangent: Vælg en cirkel eller bue og et eksternt punkt, og vælg derefter den ønskede løsning",
|
||||
"ui.construction.free_hint": "Hjælpelinje: Klik på to punkter; højreklik for at starte forfra",
|
||||
"ui.construction.normal_needs_length": "En uendelig linje er altid en hjælpelinje: Skriv en længde for at tegne en normal linje",
|
||||
"ui.construction.point_hint": "Hjælpelinje: Klik på det punkt, linjen skal gå igennem",
|
||||
"ui.construction.reference_hint": "Hjælpelinje: Vælg en ret linje og derefter et punkt; højreklik for at skifte reference",
|
||||
"ui.construction.ribbon_label": "Hjælpelinjer",
|
||||
"ui.construction.side_hint": "Hjælpelinje: Klik på den side, linjen går mod; højreklik for at starte forfra",
|
||||
"ui.context_menu": "Genvejsmenu",
|
||||
"ui.continuity_crease": "Knæk",
|
||||
"ui.continuity_curvature": "Krumning",
|
||||
"ui.continuity_curvature_change": "Ændring i krumning",
|
||||
"ui.continuity_g0": "G0-knæk",
|
||||
"ui.continuity_g1": "G1-tangentkontinuitet",
|
||||
"ui.continuity_g2": "G2-krumningskontinuitet",
|
||||
"ui.continuity_gap": "Åbning mellem flader",
|
||||
"ui.continuity_position": "Positionskontinuitet",
|
||||
"ui.continuity_tangent": "Tangentkontinuitet",
|
||||
"ui.copy": "Kopiér",
|
||||
"ui.corner": "Hjørne",
|
||||
"ui.corners": "Hjørner",
|
||||
@@ -4675,6 +4749,7 @@
|
||||
"ui.hideOrigin": "Skjul nulpunkt",
|
||||
"ui.hidePlanes": "Skjul planer",
|
||||
"ui.hideProfile": "Skjul profil",
|
||||
"ui.hide_component_history": "Skjul dens historik",
|
||||
"ui.hide_origin_by_default": "Skjul nulpunktet som standard",
|
||||
"ui.hide_origin_note": "Nye designs starter med mappen Nulpunkt skjult. Et design beholder sin egen indstilling for nulpunktet, når du har vist eller skjult det.",
|
||||
"ui.history_back": "Gå ét trin tilbage",
|
||||
@@ -4682,6 +4757,7 @@
|
||||
"ui.history_edge_hint": "dobbeltklik på kanten",
|
||||
"ui.history_first": "Gå til begyndelsen",
|
||||
"ui.history_forward": "Gå ét trin frem",
|
||||
"ui.history_hide_hidden": "Skjul skjulte komponenter",
|
||||
"ui.history_hide_names": "Skjul navne",
|
||||
"ui.history_last": "Gå til slutningen",
|
||||
"ui.history_marker_tip": "Historikmarkør: Træk for at rulle modellen tilbage; nye features indsættes her",
|
||||
@@ -4691,10 +4767,12 @@
|
||||
"ui.history_play_button_show": "Vis afspilningsknappen",
|
||||
"ui.history_play_finish_action": "Afslut den aktuelle handling, før historikken afspilles",
|
||||
"ui.history_play_roll_back": "Flyt historikmarkøren tilbage, før du afspiller",
|
||||
"ui.history_show_hidden": "Vis skjulte komponenter",
|
||||
"ui.history_show_names": "Vis navne",
|
||||
"ui.history_sketch_listed_under": "Vises under {feature}",
|
||||
"ui.history_title": "Historik",
|
||||
"ui.history_toggle_names": "Vis eller skjul featurenavne",
|
||||
"ui.history_waits_for_editor": "Fortryd, Gentag og Historik venter, indtil featureeditoren er åbnet",
|
||||
"ui.hole": "Hul",
|
||||
"ui.hole_tap_type": "Hullets gevindtype",
|
||||
"ui.hole_type": "Hultype",
|
||||
@@ -4975,6 +5053,7 @@
|
||||
"ui.showDimension": "Vis mål",
|
||||
"ui.showOrigin": "Vis nulpunkt",
|
||||
"ui.showPlanes": "Vis planer",
|
||||
"ui.show_component_history": "Vis dens historik",
|
||||
"ui.show_constraints": "Bindinger",
|
||||
"ui.show_construction_geometries": "Konstruktionsgeometri",
|
||||
"ui.show_dimensions": "Mål",
|
||||
@@ -5157,7 +5236,7 @@
|
||||
"update.check_unavailable": "Søgning efter opdateringer er ikke tilgængelig for denne installation.",
|
||||
"update.checking": "Søger…",
|
||||
"update.copy": "Kopiér",
|
||||
"update.dmg_opened": "Diskbilledet er åbnet og verificeret: Træk SolidCraft til Programmer, erstat den gamle kopi, og genstart SolidCraft.",
|
||||
"update.dmg_opened": "Diskbilledet er åbnet og verificeret: Afslut SolidCraft, træk det nye SolidCraft til Programmer, erstat den gamle kopi, og åbn derefter SolidCraft igen.",
|
||||
"update.download": "Download",
|
||||
"update.download_open": "Download og åbn",
|
||||
"update.downloading": "Downloader og verificerer… {done} af {total} MB",
|
||||
@@ -5201,6 +5280,7 @@
|
||||
"web.open_link": "Åbn linket design?",
|
||||
"web.open_link_confirm": "Der er ændringer, som ikke er gemt. Skal det linkede design åbnes i en ny fane?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ vælg · Enter / Tab udfyld · Esc luk",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ vælg · Enter anvender denne værdi",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ vælg · Enter anvender det indtastede · Esc lukker",
|
||||
"⌘Enter continues on GitHub.": "⌘Enter fortsætter på GitHub."
|
||||
}
|
||||
|
||||
+163
-14
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "Rechteckig",
|
||||
"Rectangular Tube": "Rechteckrohr",
|
||||
"Reference face": "Referenzfläche",
|
||||
"Reference plane": "Referenzebene",
|
||||
"Relief Depth": "Freistellungstiefe",
|
||||
"Relief Width": "Freistellungsbreite",
|
||||
"Remove Tools": "Werkzeuge entfernen",
|
||||
@@ -494,7 +495,8 @@
|
||||
"Rename Folder": "Ordner umbenennen",
|
||||
"Render Stage": "Renderphase",
|
||||
"Reopen designs": "Designs erneut öffnen",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "Die Designs erneut öffnen, die beim letzten Beenden von SolidCraft geöffnet waren",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "Die zuletzt geöffneten Entwürfe erneut öffnen",
|
||||
"Repair Part": "Teil reparieren",
|
||||
"Replace existing library file": "Vorhandene Bibliotheksdatei ersetzen",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "Fehler melden — Screenshot prüfen und einen GitHub-Entwurf öffnen",
|
||||
"Report copied. Keep this text to submit on GitHub when you are online.": "Bericht kopiert. Bewahren Sie diesen Text auf, um ihn auf GitHub einzureichen, sobald Sie online sind.",
|
||||
@@ -789,6 +791,7 @@
|
||||
"browser.edit_canvas_title": "Leinwand bearbeiten: {name}",
|
||||
"browser.edit_point_cloud_title": "Punktwolke bearbeiten: {name}",
|
||||
"browser.move_title": "Verschieben: {name}",
|
||||
"browser.needs_repair": "{name}: muss repariert werden",
|
||||
"browser.parameters": "Parameter ({count})",
|
||||
"browser.precision": "Genauigkeit: {precision}",
|
||||
"browser.sketch_attachment_lost": "{name} (Bezug verloren)",
|
||||
@@ -833,6 +836,7 @@
|
||||
"cmd.assembly_mass_properties": "Masseeigenschaften",
|
||||
"cmd.body.print_face": "Auflagefläche",
|
||||
"cmd.body_rename": "Körper umbenennen",
|
||||
"cmd.body_repair": "Teil reparieren",
|
||||
"cmd.browser_collapse_all": "Alles einklappen",
|
||||
"cmd.browser_expand_all": "Alles ausklappen",
|
||||
"cmd.browser_group": "Neue Gruppe",
|
||||
@@ -915,6 +919,8 @@
|
||||
"cmd.diagnostics_crash_reports": "Absturzberichte",
|
||||
"cmd.diagnostics_crash_reports_clear": "Absturzberichte löschen",
|
||||
"cmd.diagnostics_crash_test": "Absturztest",
|
||||
"cmd.diagnostics_profile": "Profil",
|
||||
"cmd.diagnostics_validation": "Prüfung",
|
||||
"cmd.doc_recover": "Konstruktion wiederherstellen",
|
||||
"cmd.doc_recovery_discard": "Wiederhergestellte Konstruktion verwerfen",
|
||||
"cmd.doc_recovery_list": "Wiederherstellbare Konstruktionen",
|
||||
@@ -1355,6 +1361,7 @@
|
||||
"crash.unclean_explanation": "Beim späteren Start erkannt; der genaue Zeitpunkt des Beendens wurde nicht aufgezeichnet. Auch ein Herunterfahren des Systems oder ein erzwungener Stopp kann ein unsauberes Beenden verursachen.",
|
||||
"delete.and_more": "… und {count} weitere",
|
||||
"diagnostic.empty_boolean_component": "In Komponente '{component}' gibt es keinen Körper zum Abziehen oder für eine Schnittmenge; das Werkzeug überschneidet sich eventuell mit '{body}' in Komponente '{other}' – aktivieren Sie diese Komponente oder wählen Sie den Körper als Ziel",
|
||||
"diagnostic.reselect_reference": "{feature}: Die Referenz `{reference}` existiert nicht mehr so, wie sie ausgewählt wurde; wählen Sie sie erneut aus",
|
||||
"dialog.bodies_count": "{count} Körper",
|
||||
"dialog.clearance_invalid": "der Abstand muss eine Länge von 0 oder mehr sein",
|
||||
"dialog.compute": "Berechnen",
|
||||
@@ -1420,6 +1427,14 @@
|
||||
"file_load.reading_percent": "Datei wird gelesen... {percent} %",
|
||||
"file_load.rebuilding": "Merkmal {done} von {total} wird neu aufgebaut",
|
||||
"file_load.recovering": "Design wird wiederhergestellt",
|
||||
"file_load.step_build": "Modell wird aufgebaut",
|
||||
"file_load.step_feature": "Merkmal {done} von {total}",
|
||||
"file_load.step_part": "Teil {done} von {total}",
|
||||
"file_load.step_percent": "{percent} %",
|
||||
"file_load.step_read": "Datei wird gelesen",
|
||||
"file_load.step_rebuild": "Merkmale werden neu aufgebaut",
|
||||
"file_load.step_repair": "Teile werden geprüft und repariert",
|
||||
"file_load.step_show": "Ansicht wird vorbereitet",
|
||||
"file_load.stopping": "Warten, bis der vorherige Vorgang beendet ist...",
|
||||
"file_load.updating_links": "Verknüpfungen werden aktualisiert",
|
||||
"file_load.waiting": "Warten auf Dateivorgang",
|
||||
@@ -1801,6 +1816,27 @@
|
||||
"home.start_hint": "Erstellen Sie einen Entwurf, öffnen Sie eine zuletzt verwendete Datei oder erkunden Sie ein Beispiel.",
|
||||
"home.whats_new": "Neuigkeiten",
|
||||
"home.yesterday": "Gestern",
|
||||
"import_report.details": "Details:",
|
||||
"import_report.file_one": "Die Datei hatte 1 weiteres Problem.",
|
||||
"import_report.file_other": "Die Datei hatte {count} weitere Probleme.",
|
||||
"import_report.hint": "Die Teile erneut aus dem Programm zu exportieren, mit dem sie erstellt wurden, kann das beheben. „Warnmeldung kopieren“ enthält die Details.",
|
||||
"import_report.left_out_one": "1 Fläche konnte nicht repariert werden und fehlt.",
|
||||
"import_report.left_out_other": "{count} Flächen konnten nicht repariert werden und fehlen.",
|
||||
"import_report.needs_repair_one": "1 Teil muss repariert werden, bevor daran modelliert werden kann.",
|
||||
"import_report.needs_repair_other": "{count} Teile müssen repariert werden, bevor daran modelliert werden kann.",
|
||||
"import_report.not_imported_one": "1 Teil konnte nicht importiert werden.",
|
||||
"import_report.not_imported_other": "{count} Teile konnten nicht importiert werden.",
|
||||
"import_report.not_shown_one": "1 Fläche konnte nicht gezeichnet werden und ist ausgeblendet.",
|
||||
"import_report.not_shown_other": "{count} Flächen konnten nicht gezeichnet werden und sind ausgeblendet.",
|
||||
"import_report.open_one": "1 Teil hat Lücken zwischen seinen Flächen und kam als offener Körper herein.",
|
||||
"import_report.open_other": "{count} Teile haben Lücken zwischen ihren Flächen und kamen als offene Körper herein.",
|
||||
"import_report.refused_one": "1 Teil konnte nicht zu einem Volumenkörper werden und wurde nicht importiert.",
|
||||
"import_report.refused_other": "{count} Teile konnten nicht zu Volumenkörpern werden und wurden nicht importiert.",
|
||||
"import_report.repaired_one": "Kleine Fehler wurden an 1 Teil repariert.",
|
||||
"import_report.repaired_other": "Kleine Fehler wurden an {count} Teilen repariert.",
|
||||
"import_report.solids": "{solids} von {parts} Teilen als Volumenkörper importiert.",
|
||||
"import_report.surfaces_one": "1 Teil konnte nicht zu einem Volumenkörper werden und kam als Flächen herein.",
|
||||
"import_report.surfaces_other": "{count} Teile konnten nicht zu Volumenkörpern werden und kamen als Flächen herein.",
|
||||
"install.button_tip": "SolidCraft als App installieren: eigenes Fenster, funktioniert auch offline",
|
||||
"install.dock_1": "Wählen Sie Ablage › Zum Dock hinzufügen.",
|
||||
"install.dock_2": "Klicken Sie auf „Hinzufügen“.",
|
||||
@@ -2088,12 +2124,19 @@
|
||||
"parts.family.button_head_cap_screw": "Linsenkopfschraube mit Innensechskant",
|
||||
"parts.family.dowel_pin": "Zylinderstift",
|
||||
"parts.family.hex_bolt": "Sechskantschraube",
|
||||
"parts.family.hex_cap_screw_inch": "Sechskantschraube (Zoll)",
|
||||
"parts.family.hex_nut": "Sechskantmutter",
|
||||
"parts.family.hex_nut_inch": "Sechskantmutter (Zoll)",
|
||||
"parts.family.set_screw": "Gewindestift mit Innensechskant und Kegelstumpf",
|
||||
"parts.family.socket_head_cap_screw": "Zylinderschraube mit Innensechskant",
|
||||
"parts.family.washer": "Unterlegscheibe",
|
||||
"parts.family.washer_sae": "Unterlegscheibe, SAE (schmal)",
|
||||
"parts.family.washer_uss": "Unterlegscheibe, USS (breit)",
|
||||
"parts.holes": "Bohrungen (optional)",
|
||||
"parts.placement_hint": "Wählen Sie eine oder mehrere Bohrungen, um in jede ein Teil einzusetzen, oder OK platziert es im Ursprung",
|
||||
"parts.system": "Maßsystem",
|
||||
"parts.system.inch": "Zoll",
|
||||
"parts.system.metric": "Metrisch",
|
||||
"pattern.alignment_2": "Ausrichtung 2",
|
||||
"pattern.alignment_hint": "Die Anzahl enthält das Original. Die zentrierte Anordnung verteilt Kopien auf beide Seiten; bei gerader Anzahl legt die Richtungsumkehr die Seite mit der zusätzlichen Kopie fest.",
|
||||
"pattern.direction_1": "Richtung 1",
|
||||
@@ -2132,6 +2175,18 @@
|
||||
"prefs.alt_frees_manipulators": "Alt / Option schaltet auch an Manipulatorpfeilen das Einrasten ab",
|
||||
"prefs.anti_aliasing_amd_dx12": "Die Kantenglättung verwendet 1×, da der Starttest mit Multisampling keine Ausgabe erzeugt hat, während der Test mit 1× erfolgreich war. Sie können oben eine andere Einstellung wählen. Starten Sie SolidCraft neu, damit die Änderung übernommen wird, sofern der Test erfolgreich ist.",
|
||||
"prefs.anti_aliasing_note": "Glättet gezackte Kanten.",
|
||||
"prefs.appearance.colour_vision_help": "Ändert die bedeutungstragenden Farben: Skizzenzustände, Achsen, Biegungen, Analysen und Symbole.",
|
||||
"prefs.appearance.desktop_only": "Nur Desktop",
|
||||
"prefs.appearance.follow_help": "Wird neu geladen, wenn ein anderes Werkzeug die Datei überschreibt.",
|
||||
"prefs.appearance.interface_scale": "Oberflächenskalierung",
|
||||
"prefs.appearance.next_themes": "Nächste Designs",
|
||||
"prefs.appearance.option_count": "{count} Optionen",
|
||||
"prefs.appearance.option_count_one": "{count} Option",
|
||||
"prefs.appearance.previous_themes": "Vorherige Designs",
|
||||
"prefs.appearance.theme_count": "{count} Designs",
|
||||
"prefs.appearance.theme_own": "Eigene Designfarben",
|
||||
"prefs.appearance.viewport_colours": "Farben der 3D-Ansicht",
|
||||
"prefs.appearance.viewport_logo": "Ansichtsfenster und Logo",
|
||||
"prefs.applies_on_restart": "Wird nach einem Neustart übernommen",
|
||||
"prefs.assistant": "Assistent",
|
||||
"prefs.auto_operation": "Operation automatisch auswählen",
|
||||
@@ -2146,6 +2201,8 @@
|
||||
"prefs.bottom_centre": "Unten mittig",
|
||||
"prefs.bottom_right": "Unten rechts",
|
||||
"prefs.browser": "Browser",
|
||||
"prefs.browser_components_open": "Komponenten zu Beginn aufgeklappt",
|
||||
"prefs.browser_components_open_note": "Jede Komponente im Browser ist wie bisher von Anfang an aufgeklappt, statt zugeklappt zu bleiben, bis du sie aufklappst.",
|
||||
"prefs.browser_eye_left": "Auge neben dem Aufklapppfeil",
|
||||
"prefs.browser_eye_left_note": "Browserzeilen zeigen ihr Auge zum Ein- und Ausblenden wie bisher neben dem Aufklapppfeil statt in einer Spalte am rechten Zeilenende.",
|
||||
"prefs.browser_reveal_on_hover": "Beim Überfahren einblenden",
|
||||
@@ -2195,7 +2252,19 @@
|
||||
"prefs.font_sample": "Zwölf Boxkämpfer jagen Viktor quer über den großen Sylter Deich · Ø12,50 mm · 45,0° · 0123456789",
|
||||
"prefs.font_unreadable": "Die ausgewählte Schriftdatei konnte nicht gelesen werden.",
|
||||
"prefs.general": "Allgemein",
|
||||
"prefs.general_note": "Sprache, Speichern und Updates.",
|
||||
"prefs.general.active": "Aktiv",
|
||||
"prefs.general.crash_reports": "Absturzberichte",
|
||||
"prefs.general.edited": "Geändert",
|
||||
"prefs.general.held": "Enthalten",
|
||||
"prefs.general.import": "Importieren...",
|
||||
"prefs.general.last_check": "Zuletzt geprüft: {time}",
|
||||
"prefs.general.minutes": "Minuten",
|
||||
"prefs.general.name_setup": "Dieses Setup benennen",
|
||||
"prefs.general.profile_name": "Profilname",
|
||||
"prefs.general.save_new": "Als neu speichern",
|
||||
"prefs.general.step_import": "STEP und Import",
|
||||
"prefs.general.this_computer": "Dieser Computer",
|
||||
"prefs.general_note": "Profile, Sprache, Dateien und Updates.",
|
||||
"prefs.gesture_orbit": "Ansicht drehen",
|
||||
"prefs.gesture_pan": "Ansicht verschieben",
|
||||
"prefs.gesture_zoom": "Ansicht zoomen",
|
||||
@@ -2250,10 +2319,10 @@
|
||||
"prefs.logo_accent": "Akzentfarbe",
|
||||
"prefs.logo_theme": "Designfarben",
|
||||
"prefs.max_file_size": "Maximale Dateigröße zum Öffnen",
|
||||
"prefs.max_file_size_design": "Designdateien",
|
||||
"prefs.max_file_size_design": "Größte zu öffnende Datei: Entwurf",
|
||||
"prefs.max_file_size_flag": "Dieser Lauf wurde mit --max-file-size {size} gestartet. Der Wert gilt, bis SolidCraft geschlossen wird. Diese Einstellungen gelten beim Start ohne diese Option.",
|
||||
"prefs.max_file_size_note": "Standard: 512 MB und 256 MB; höchstens 16 GB. Das Öffnen einer Datei benötigt ein Mehrfaches ihrer Größe im Arbeitsspeicher. Ein Design mit großem Import benötigt zum Speichern und erneuten Öffnen eventuell eine höhere Grenze für Designdateien.",
|
||||
"prefs.max_file_size_step": "STEP- und Importdateien",
|
||||
"prefs.max_file_size_step": "Größte zu öffnende Datei: STEP und Import",
|
||||
"prefs.max_file_size_web": "Die Web-App öffnet STEP- und Importdateien bis 512 MB und Designs bis 256 MB: Ein Browser-Tab kann keine größeren Dateien aufnehmen. In der Desktop-App lassen sich diese Grenzen erhöhen.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "Große Symbole",
|
||||
@@ -2270,7 +2339,7 @@
|
||||
"prefs.navigator_fit_note": "Eine Fläche, Ecke oder ein Pfeil des Würfels oder eine Achse des Gizmos blickt auch auf die Mitte des Modells, aus dem einen Abstand, der es von jeder Seite einpasst (seine Hüllkugel). Home nutzt denselben Zoom und dieselbe Mitte, solange dies an ist, sodass danach gewählte Ansichten sich nur drehen. Aus: Die Ansicht dreht sich an Ort und Stelle und behält Ziel und Zoom.",
|
||||
"prefs.network_host_remove": "Entfernen",
|
||||
"prefs.network_locations": "Netzwerkspeicherorte in Designs zulassen",
|
||||
"prefs.network_locations_note": "Deaktiviert: Verknüpfte Dateien eines Designs auf einem anderen Computer (\\\\server\\share) werden nur von den hier aufgeführten Servern geöffnet oder nachdem Sie einen Server auf Nachfrage ausdrücklich zugelassen haben.",
|
||||
"prefs.network_locations_note": "Für Organisationen, die Entwürfe auf Dateiservern speichern. Aus: nur erlaubte Server.",
|
||||
"prefs.network_locations_section": "Netzwerkspeicherorte",
|
||||
"prefs.not_on_gpu": "Auf dieser GPU nicht verfügbar",
|
||||
"prefs.on": "An",
|
||||
@@ -2296,10 +2365,12 @@
|
||||
"prefs.profile_applied": "Profil angewendet.",
|
||||
"prefs.profile_changed": "Die Einstellungen wurden seit dem Speichern des Profils geändert. „Speichern“ übernimmt sie ins Profil.",
|
||||
"prefs.profile_deleted": "Profil gelöscht.",
|
||||
"prefs.profile_export": "Exportieren…",
|
||||
"prefs.profile_export": "Exportieren",
|
||||
"prefs.profile_exported": "Profil exportiert.",
|
||||
"prefs.profile_imported": "Profil importiert. Unter „Aktives Profil“ auswählen, um dazu zu wechseln.",
|
||||
"prefs.profile_imported": "Profil importiert.",
|
||||
"prefs.profile_name_hint": "Profilname, z. B. Laptop",
|
||||
"prefs.profile_operations": "Vorgänge profilieren",
|
||||
"prefs.profile_operations_note": "Zeichnet auf, wie lange SMLib-Aufrufe, Kernel-Vorgänge, Features, Befehle und Importe dauern. Agenten lesen es mit diagnostics.profile.",
|
||||
"prefs.profile_rename": "Umbenennen",
|
||||
"prefs.profile_renamed": "Profil umbenannt.",
|
||||
"prefs.profile_save": "Speichern",
|
||||
@@ -2318,12 +2389,13 @@
|
||||
"prefs.render_previous_geometry_budget": "Bisheriges Geometriebudget für die Render-Vorschau verwenden",
|
||||
"prefs.render_previous_geometry_budget_help": "Stellt die bisherige feste Obergrenze für die Geometrie wieder her (512 MiB auf 64-Bit-Desktop-Systemen, andernfalls 128 MiB) anstelle des geräteabhängigen Budgets.",
|
||||
"prefs.reports_saved": "{n} gespeichert",
|
||||
"prefs.restore_page_defaults": "Seitenstandardwerte wiederherstellen",
|
||||
"prefs.ribbon_click_ends_tool": "Klick auf die Schaltfläche des laufenden Werkzeugs beendet es",
|
||||
"prefs.ribbon_click_ends_tool_note": "Ein Klick auf die hervorgehobene Schaltfläche des aktiven Skizzenwerkzeugs beendet das Werkzeug, sodass Klicks in die Ansicht wieder auswählen. Aus: Der Klick startet das Werkzeug neu.",
|
||||
"prefs.ribbon_reveal": "Suchergebnisse im Menüband zeigen",
|
||||
"prefs.ribbon_reveal_note": "Steht der Zeiger über einem Ergebnis in Befehle suchen, zeigt das Menüband, wo der Befehl liegt: seine Registerkarte, seine Schaltfläche oder seine Zeile im Aufklappmenü eines Panels. Ein aus der Suche gestarteter Befehl blinkt dort, und ein laufender Befehl aus einem Aufklappmenü markiert sein Panel.",
|
||||
"prefs.right_click": "Rechtsklick",
|
||||
"prefs.saving": "Automatisches Speichern und Wiederherstellung",
|
||||
"prefs.saving": "Starten und Speichern",
|
||||
"prefs.scale_note": "Die Skalierung lässt sich auch mit Strg + / Strg − im Fenster ändern. Bei 100 % sind die Schaltflächen 28 px groß.",
|
||||
"prefs.scale_text": "Skalierung und Text",
|
||||
"prefs.search": "Einstellungen suchen",
|
||||
@@ -2442,6 +2514,12 @@
|
||||
"prefs.under_cursor": "Unter dem Cursor",
|
||||
"prefs.units_precision": "Einheiten und Genauigkeit",
|
||||
"prefs.updates": "Updates",
|
||||
"prefs.validation": "Körperprüfung",
|
||||
"prefs.validation.each_feature": "Einmal je Feature",
|
||||
"prefs.validation.each_step": "Nach jedem Schritt",
|
||||
"prefs.validation.label": "Vollständige Kernel-Prüfung",
|
||||
"prefs.validation.off": "Aus (experimentell)",
|
||||
"prefs.validation_note": "Nach jedem Schritt: prüft jeden Sweep, jede Boolesche Operation und jede Rundung.\nEinmal je Feature: prüft das Ergebnis jedes Features einmal.\nAus (experimentell): keine Prüfung.",
|
||||
"prefs.value_boxes_in_view": "Werte in der Ansicht neben ihren Griffen zeigen",
|
||||
"prefs.value_boxes_in_view_tip": "Der Wert eines Befehls (Abstand, Radius …) in einem Feld neben seinem Griff, über dem Modell. Aus: Sie geben ihn im Befehlsfenster ein, das nach einer Auswahl die Tastatur erhält, und nichts verdeckt das Modell.",
|
||||
"prefs.view": "Ansichtsverhalten",
|
||||
@@ -2476,6 +2554,17 @@
|
||||
"recovery.just_now": "gerade eben",
|
||||
"recovery.min_ago": "vor {count} Min.",
|
||||
"recovery.never_saved": "nie gespeichert",
|
||||
"refused.change": "{body} konnte nicht geändert werden.",
|
||||
"refused.cut": "{body} konnte nicht geschnitten werden.",
|
||||
"refused.face_curved": "Eine seiner gekrümmten Flächen kann nicht zum Modellieren verwendet werden.",
|
||||
"refused.face_flat": "Eine seiner ebenen Flächen kann nicht zum Modellieren verwendet werden.",
|
||||
"refused.face_freeform": "Eine seiner Freiformflächen kann nicht zum Modellieren verwendet werden.",
|
||||
"refused.face_rounded": "Eine seiner gerundeten Flächen kann nicht zum Modellieren verwendet werden.",
|
||||
"refused.faces_apart": "Seine Flächen treffen nicht genau genug aufeinander, um damit zu modellieren.",
|
||||
"refused.intersect": "Schnittmenge mit {body} nicht möglich.",
|
||||
"refused.join": "Konnte nicht mit {body} vereinigt werden.",
|
||||
"refused.repair": "Teil reparieren",
|
||||
"refused.show_face": "Fläche zeigen",
|
||||
"render.adaptive_absolute": "Absolute RGB-Fehleruntergrenze",
|
||||
"render.adaptive_coverage": "Zielwert für den Abdeckungsfehler",
|
||||
"render.adaptive_enable": "Samples mithilfe unabhängiger Pilot-Samples zuteilen",
|
||||
@@ -3943,11 +4032,19 @@
|
||||
"the section's profiles must be in one sketch": "die Profile des Querschnitts müssen in einer Skizze liegen",
|
||||
"the two origins are on the same component": "die beiden Ursprünge liegen auf derselben Komponente",
|
||||
"thread.automatic_size": "Automatische Größe",
|
||||
"thread.chamfer_depth_needed": "für diese Form erforderlich",
|
||||
"thread.designation": "Bezeichnung",
|
||||
"thread.direction": "Gewinderichtung",
|
||||
"thread.drill_range": "Der Bohrer des Gewindebohrerherstellers ist {drill} mm. Der genormte Kerndurchmesser des Innengewindes ist {minor} mm. Ein Bohrer von {low} bis {high} mm wird angenommen.",
|
||||
"thread.drill_range_custom": "Der Bohrer ist der Nenndurchmesser minus Steigung, {drill} mm. Der genormte Kerndurchmesser des Innengewindes ist {minor} mm. Ein Bohrer von {low} bis {high} mm wird angenommen.",
|
||||
"thread.drill_range_taper": "Der Bohrer des Gewindebohrerherstellers ist {drill} mm. Der genormte Kerndurchmesser am Gewindeende ist {minor} mm. Ein Bohrer von {low} bis {high} mm wird angenommen.",
|
||||
"thread.end_square": "Rechtwinklig",
|
||||
"thread.fade_short": "1,25 P (ISO 3508)",
|
||||
"thread.fade_standard": "2,5 P (ISO 3508)",
|
||||
"thread.form_bspp": "Rohrgewinde G (ISO 228-1)",
|
||||
"thread.form_bspt": "Kegeliges Rohrgewinde R, Rc (ISO 7-1)",
|
||||
"thread.form_npt": "Kegeliges Rohrgewinde NPT (ASME B1.20.1)",
|
||||
"thread.form_rp": "Zylindrisches Innenrohrgewinde Rp (ISO 7-1)",
|
||||
"thread.iso_metric": "ISO metrisch",
|
||||
"thread.left_hand": "Linksgewinde",
|
||||
"thread.right_hand": "Rechtsgewinde",
|
||||
"thread.select_size": "Größe wählen",
|
||||
"timeline.delete_after_marker_one": "1 Merkmal nach der Verlaufsmarkierung löschen",
|
||||
"timeline.delete_after_marker_other": "{count} Merkmale nach der Verlaufsmarkierung löschen",
|
||||
@@ -4121,6 +4218,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Führen Sie dies in einem Terminal aus und klicken Sie dann erneut auf Verbinden:",
|
||||
"ui.agent_chat_auth_title": "Beim Agenten anmelden",
|
||||
"ui.agent_chat_cancel": "Abbrechen",
|
||||
"ui.agent_chat_cancel_timeout": "Der Agent hat nicht auf Stopp reagiert und wurde getrennt. Zum Fortfahren erneut verbinden.",
|
||||
"ui.agent_chat_cannot_start": "Installiert, startet aber nicht: {error}",
|
||||
"ui.agent_chat_changes": "Änderungen: {n}, ein Rückgängig-Schritt",
|
||||
"ui.agent_chat_changes_n": "{n} Änderungen",
|
||||
@@ -4198,6 +4296,7 @@
|
||||
"ui.agent_chat_no_images": "Dieser Agent nimmt keine Bilder an",
|
||||
"ui.agent_chat_no_picture": "In der Zwischenablage befindet sich kein Bild",
|
||||
"ui.agent_chat_no_tool_call": "Das Modell hat die Werkzeuge von SolidCraft nicht verwendet, daher wurde das Design nicht geändert. Wählen Sie ein Modell, das Werkzeugaufrufe unterstützt; siehe „Lokale Modelle“ im Leitfaden zum Agenten-Chat.",
|
||||
"ui.agent_chat_node_too_old": "Node.js {version} ist für diesen Agenten zu alt. Installieren Sie Node.js 20.10 oder neuer und klicken Sie dann auf Aktualisieren.",
|
||||
"ui.agent_chat_none_found": "Kein Coding-Agent gefunden. Installieren Sie einen davon und klicken Sie dann auf Aktualisieren:",
|
||||
"ui.agent_chat_none_found_short": "Kein Agent gefunden",
|
||||
"ui.agent_chat_not_installed": "Nicht installiert",
|
||||
@@ -4447,10 +4546,16 @@
|
||||
"ui.colour_vision_sample": "Beispiel",
|
||||
"ui.colour_vision_tritan": "Tritan",
|
||||
"ui.colour_vision_tritan_tip": "Bei schwachem oder fehlendem Blausinn",
|
||||
"ui.comb_length": "Kammlänge",
|
||||
"ui.comb_length_tip": "Wie lang die Zähne sind: ein Vielfaches der automatischen Länge, bei der der längste Zahn ein Fünftel der Größe der gezeigten Kurven ist.",
|
||||
"ui.comb_teeth": "Zähne",
|
||||
"ui.comb_teeth_tip": "Wie viele Zähne jeder Kamm hat, gleichmäßig entlang der Kurve verteilt. Mehr Zähne zeigen genauer, wie sich die Krümmung ändert.",
|
||||
"ui.combine": "Kombinieren",
|
||||
"ui.coming_later": "bald verfügbar",
|
||||
"ui.command_done": "{command} fertig",
|
||||
"ui.command_palette_hint": "Befehl eingeben, z. B. {example} — oder S drücken, um zu suchen",
|
||||
"ui.components_from_bodies_hint": "Für jeden ausgewählten Körper eine gleichnamige Komponente unter der aktiven Komponente erstellen.",
|
||||
"ui.components_from_bodies_required": "Mindestens einen Körper auswählen.",
|
||||
"ui.concentric": "Konzentrisch",
|
||||
"ui.cone": "Kegel",
|
||||
"ui.conic_curve": "Kegelschnitt",
|
||||
@@ -4469,11 +4574,24 @@
|
||||
"ui.construction.ribbon_label": "Hilfslinien",
|
||||
"ui.construction.side_hint": "Konstruktionslinie: die Seite anklicken, zu der die Linie läuft; Rechtsklick beginnt neu",
|
||||
"ui.context_menu": "Kontextmenü",
|
||||
"ui.continuity_crease": "Knick",
|
||||
"ui.continuity_curvature": "Krümmung",
|
||||
"ui.continuity_curvature_change": "Krümmungsänderung",
|
||||
"ui.continuity_curvature_change_tip": "Krümmungsänderung (G3): Auch die Krümmung ändert sich an der Naht glatt.",
|
||||
"ui.continuity_curvature_tip": "Krümmungsstetig (G2): Spiegelungen laufen ohne Knick hindurch.",
|
||||
"ui.continuity_g0": "G0 Knick",
|
||||
"ui.continuity_g1": "G1 Tangente",
|
||||
"ui.continuity_g2": "G2 Krümmung",
|
||||
"ui.continuity_gap": "Lücke",
|
||||
"ui.continuity_position": "Position",
|
||||
"ui.continuity_position_tip": "Position (G0): Die Flächen treffen sich, mit einem Knick an der Naht.",
|
||||
"ui.continuity_smooth_curvature_change": "Glatte Krümmungsänderung",
|
||||
"ui.continuity_smooth_curvature_change_tip": "Glatte Krümmungsänderung (G4): Selbst die Art, wie sich die Krümmung ändert, läuft an der Naht glatt weiter.",
|
||||
"ui.continuity_tangent": "Tangential",
|
||||
"ui.continuity_tangent_tip": "Tangential (G1): kein Knick, aber die Krümmung springt an der Naht.",
|
||||
"ui.copy": "Kopieren",
|
||||
"ui.copy_details": "Details kopieren",
|
||||
"ui.copy_details_tip": "Die vollständige Meldung kopieren, um sie nachzuschlagen oder zu melden.",
|
||||
"ui.corner": "Ecke",
|
||||
"ui.corners": "Ecken",
|
||||
"ui.cosmetic_thread_markings": "Kosmetische Gewindemarkierungen",
|
||||
@@ -4591,6 +4709,7 @@
|
||||
"ui.edit_sketch_empty": "Keine Skizzen zum Bearbeiten",
|
||||
"ui.ellipse": "Ellipse",
|
||||
"ui.emboss_wrap_note": "Senkrechte Linienprofile, die einen unterstützten Zylinder berühren, werden automatisch aufgewickelt. Eine Wand auswählen, um ein Ziel festzulegen.",
|
||||
"ui.end": "Ende",
|
||||
"ui.end_radius": "Endradius",
|
||||
"ui.end_scale": "Endskalierung",
|
||||
"ui.entities": "Elemente",
|
||||
@@ -4620,11 +4739,39 @@
|
||||
"ui.extrude_auto_to_object": "Bis zu einem gewählten Ziel extrudieren",
|
||||
"ui.extrude_auto_to_object_tip": "Wenn in „Extrudieren“ ein Profil gewählt ist, schaltet das Wählen einer Fläche, eines Eckpunkts oder einer parallelen Ebene außerhalb der Profilebene die Ausdehnung auf „Bis Objekt“ um",
|
||||
"ui.factor": "Faktor",
|
||||
"ui.fade": "Auslauf",
|
||||
"ui.fade_length": "Auslauflänge",
|
||||
"ui.feature_history": "Feature-Verlauf",
|
||||
"ui.feature_options": "Funktionsoptionen",
|
||||
"ui.features": "Funktionen",
|
||||
"ui.file": "Datei",
|
||||
"ui.fillet": "Vollrundung",
|
||||
"ui.fillet_corner_ball": "Kugel",
|
||||
"ui.fillet_corner_ball_tip": "Ein Stück der Kugel des rollenden Balls, tangential (G1) zu jeder Rundung.",
|
||||
"ui.fillet_corner_curvature_tip": "Krümmung (G2): ein glattes Flächenstück. Die Ecke wird entlang jeder Kante um den Rücksprung zurückgesetzt ({factor} × der Radius, sofern Sie keinen eingeben) und mit einem Flächenstück gefüllt, sodass Spiegelungen ohne Knick hindurchlaufen.",
|
||||
"ui.fillet_corner_needs_constant": "Ecken mit glattem Flächenstück (G2) brauchen einen konstanten Radius und exakte Radiusanpassung.",
|
||||
"ui.fillet_corner_patch": "Glattes Flächenstück",
|
||||
"ui.fillet_corner_patch_tip": "Krümmungsstetig (G2): Die Ecke wird entlang jeder Kante zurückgesetzt und mit einem Flächenstück gefüllt, sodass Spiegelungen ohne Knick hindurchlaufen.",
|
||||
"ui.fillet_corner_tangent_tip": "Tangential (G1): eine Kugelecke, ein Stück der Kugel des rollenden Balls, tangential zu jeder Rundung.",
|
||||
"ui.fillet_error_corner": "Die glatte Ecke ließ sich hier nicht bauen. Versuchen Sie einen anderen Rücksprung oder tangentiale Ecken.",
|
||||
"ui.fillet_error_failed": "Diese Rundung ließ sich an diesen Kanten nicht bauen. Versuchen Sie einen anderen Radius oder runden Sie weniger Kanten auf einmal.",
|
||||
"ui.fillet_error_setback_short": "Der Rücksprung ist für das Eckstück zu kurz: Es lässt sich ab {least} bauen. Versuchen Sie einen längeren Rücksprung.",
|
||||
"ui.fillet_error_too_large": "Der Radius ist zu groß für die Flächen neben diesen Kanten. Versuchen Sie einen kleineren Radius oder stellen Sie „Flächengrenzen“ auf „Größter Radius“.",
|
||||
"ui.fillet_fit": "Flächengrenzen",
|
||||
"ui.fillet_fit_exact": "Exakter Radius",
|
||||
"ui.fillet_fit_needs_constant": "Radiusanpassung braucht einen konstanten Radius.",
|
||||
"ui.fillet_fit_reduce": "Größter Radius",
|
||||
"ui.fillet_fit_taper": "Verjüngter Radius",
|
||||
"ui.fillet_fit_tip": "Wenn der Radius größer ist, als die Flächen neben einer Kante zulassen.\nExakter Radius: der angegebene Radius, oder keine Rundung und eine Meldung mit dem Grund.\nGrößter Radius: jede Kante mit dem größten passenden Radius gerundet.\nVerjüngter Radius: der Radius bleibt, die Rundung wird nur dort verjüngt, wo die Flächen enden.",
|
||||
"ui.fillet_options_tip": "Tangentenkette: auch die Kanten runden, die glatt an die gewählten anschließen.\nVorschau: das Ergebnis beim Bearbeiten in der Ansicht zeigen.",
|
||||
"ui.fillet_setback": "Rücksprung",
|
||||
"ui.fillet_setback_hint": "{factor} × Radius",
|
||||
"ui.fillet_setback_least": "Mehr als {least}",
|
||||
"ui.fillet_setback_tip": "Wo das glatte Eckflächenstück beginnt: wie weit von der Ecke entlang jeder Kante. Standard ist {factor} × der Radius. Er muss größer sein als der kleinste Rücksprung, wo sich die Rundungen ohne ihn treffen: der Radius, wo die Flächen rechtwinklig zueinander stehen.",
|
||||
"ui.fillet_smooth_radius": "Radius glatt ändern",
|
||||
"ui.fillet_type_choices_tip": "Konstanter Radius: ein Radius entlang der ganzen Kante.\nSehnenlänge: die Breite der Rundung bleibt gleich, in welchem Winkel die Flächen auch aufeinandertreffen.\nVariabler Radius: ein Radius an jedem Ende und weitere entlang der Kante, wenn Sie sie hinzufügen.\nKrümmungsstetig: ein Querschnitt ohne Knick in den Spiegelungen, wo er auf ebene Flächen trifft (G2).\nSpline: der Radius entlang der Kante folgt Steuerradien, die Sie festlegen.",
|
||||
"ui.fillet_type_curvature": "Krümmungsstetig",
|
||||
"ui.fillet_type_tip": "Der Querschnitt über die Kante. Krümmungsstetig: ein krümmungsstetiger Querschnitt (G2) zwischen ebenen Flächen, sodass Spiegelungen ohne Knick von den Flächen in die Rundung laufen.",
|
||||
"ui.filter_commands_or_keys": "Befehle oder Tasten filtern",
|
||||
"ui.findInTimeline": "In der Zeitleiste suchen",
|
||||
"ui.findInWindow": "Im Fenster suchen",
|
||||
@@ -4753,6 +4900,7 @@
|
||||
"ui.history_sketch_listed_under": "Aufgeführt unter {feature}",
|
||||
"ui.history_title": "Verlauf",
|
||||
"ui.history_toggle_names": "Merkmalsnamen ein- oder ausblenden",
|
||||
"ui.history_waits_for_editor": "Rückgängig, Wiederholen und Verlauf warten, bis der Feature-Editor geöffnet ist",
|
||||
"ui.hole": "Bohrung",
|
||||
"ui.hole_tap_type": "Gewindetyp",
|
||||
"ui.hole_type": "Bohrungstyp",
|
||||
@@ -4872,6 +5020,7 @@
|
||||
"ui.part_size_needs_a_part": "Wählen Sie ein eingefügtes Standardbauteil (Schraube, Mutter, Scheibe oder Stift) aus, um seine Größe zu ändern. Einen Körper vergrößern oder verkleinern Sie mit Skalieren.",
|
||||
"ui.part_size_shared": "Baut das Teil neu auf und ersetzt alle hinzugefügten Merkmale. Alle Vorkommen dieser Komponente werden aktualisiert.",
|
||||
"ui.paste": "Einfügen",
|
||||
"ui.patch_continuity_corner_tip": "Der Patch schließt entlang jeder Seite mit der gewählten Stetigkeit an die umgebenden Flächen an. An einer Ecke, an der diese Flächen mit unterschiedlicher Krümmung aufeinandertreffen, bleibt er dort tangential (G1).",
|
||||
"ui.path": "Pfad",
|
||||
"ui.pattern_object_type_tip": "Körper: feste Formen, in der Ansicht oder im Browser gewählt; jeder wird ganz kopiert.\nFunktionen: Schritte im Verlauf, etwa eine Extrusion oder eine Bohrung, im Verlauf oder auf einer von ihnen erzeugten Fläche gewählt; der Schritt wird wiederholt.\nEine Skizze ist keines von beiden: Ihre Kurven mit Muster oder Spiegeln in der Skizze wiederholen oder sie zuerst extrudieren.",
|
||||
"ui.pattern_on_path": "Pfadmuster",
|
||||
@@ -5064,7 +5213,7 @@
|
||||
"ui.slicer_no_response": "{slicer} hat nicht rechtzeitig geantwortet. Die Datei ist bereits unter {path} gespeichert: Senden Sie sie erneut, sobald der Slicer gestartet ist.",
|
||||
"ui.slicer_resend_hint": "Erneutes Senden ersetzt die Datei, die der Slicer geöffnet hat.",
|
||||
"ui.slicer_single_instance": "Dateien in einem bereits laufenden Slicer öffnen",
|
||||
"ui.slicer_single_instance_tip": "PrusaSlicer, SuperSlicer und Cura laden die Datei in das geöffnete Fenster, statt ein weiteres zu starten. Andere Slicer werden wie gewohnt gestartet. PrusaSlicer 3.0 tut das immer: Sein Starter kann eine Datei nicht anders übernehmen.",
|
||||
"ui.slicer_single_instance_tip": "Für Slicer, die Dateien in ein laufendes Fenster laden.",
|
||||
"ui.slicer_starting": "{slicer} wird gestartet…",
|
||||
"ui.slicer_still_starting": "Es wird weiter darauf gewartet, dass {slicer} startet. Sonst wurde nichts gesendet.",
|
||||
"ui.slicer_stl_fallback": "Das 3MF konnte nicht geschrieben werden, daher ist dies ein STL.",
|
||||
@@ -5209,13 +5358,13 @@
|
||||
"update.available": "SolidCraft {version} ist verfügbar",
|
||||
"update.button": "Update",
|
||||
"update.button_version": "Auf {version} aktualisieren",
|
||||
"update.check_automatically": "Automatisch nach Updates suchen",
|
||||
"update.check_automatically": "Beim Start und einmal täglich prüfen",
|
||||
"update.check_for_updates": "Nach Updates suchen",
|
||||
"update.check_now": "Jetzt suchen",
|
||||
"update.check_unavailable": "Für diese Installation ist keine Update-Prüfung verfügbar.",
|
||||
"update.checking": "Suche läuft…",
|
||||
"update.copy": "Kopieren",
|
||||
"update.dmg_opened": "Das Disk-Image ist geöffnet und geprüft: SolidCraft in „Programme“ ziehen, die alte Version ersetzen und SolidCraft neu starten.",
|
||||
"update.dmg_opened": "Das Disk-Image ist geöffnet und geprüft: SolidCraft beenden, das neue SolidCraft in „Programme“ ziehen, die alte Version ersetzen und SolidCraft wieder öffnen.",
|
||||
"update.download": "Herunterladen",
|
||||
"update.download_open": "Herunterladen und öffnen",
|
||||
"update.downloading": "Herunterladen und prüfen… {done} von {total} MB",
|
||||
|
||||
@@ -73,15 +73,16 @@
|
||||
"navbar.custom.position_centre": "Centre",
|
||||
"navbar.description.separator": "A thin line that groups neighbouring slots.",
|
||||
"pattern.alignment_hint": "Counts include the original. Centre balances copies; Flip chooses the extra side for even totals.",
|
||||
"prefs.appearance.colour_vision_help": "Changes the colours that carry a meaning: sketch states, axes, bends, analyses and icons.",
|
||||
"prefs.appearance.viewport_colours": "3D view colours",
|
||||
"prefs.bottom_centre": "Bottom centre",
|
||||
"prefs.logo_theme": "Theme's colours",
|
||||
"prefs.max_file_size": "Largest file to open",
|
||||
"prefs.max_file_size_design": "Design files",
|
||||
"prefs.max_file_size_flag": "This run started with --max-file-size {size}, which applies until SolidCraft closes. These settings apply when it starts without it.",
|
||||
"prefs.max_file_size_note": "Defaults: 512 MB and 256 MB; at most 16 GB. Opening a file takes several times its size in memory. A design that keeps a large import may need a higher design file limit to save and reopen.",
|
||||
"prefs.max_file_size_step": "STEP and import files",
|
||||
"prefs.max_file_size_web": "The web app opens STEP and import files up to 512 MB and designs up to 256 MB: a browser tab can't hold larger files. The desktop app can raise these limits.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.network_locations_note": "For organisations that keep designs on file servers. Off: only servers you allow.",
|
||||
"prefs.sketch_conflict_colour": "Conflicting geometry in the conflict colour",
|
||||
"prefs.sketch_conflict_colour_note": "While a sketch's constraints conflict, the curves and points a failing constraint holds are drawn in the conflict colour too, not only its glyph or dimension. Off: they keep their usual colours.",
|
||||
"prefs.sketch_construction_lines": "Extended construction line tools",
|
||||
|
||||
+188
-14
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "Rectangular",
|
||||
"Rectangular Tube": "Rectangular Tube",
|
||||
"Reference face": "Reference face",
|
||||
"Reference plane": "Reference plane",
|
||||
"Relief Depth": "Relief Depth",
|
||||
"Relief Width": "Relief Width",
|
||||
"Remove Tools": "Remove Tools",
|
||||
@@ -494,7 +495,8 @@
|
||||
"Rename Folder": "Rename Folder",
|
||||
"Render Stage": "Render Stage",
|
||||
"Reopen designs": "Reopen designs",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "Reopen the designs that were open when SolidCraft was last closed",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "Reopen the designs that were open last time",
|
||||
"Repair Part": "Repair Part",
|
||||
"Replace existing library file": "Replace existing library file",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "Report a bug — review a screenshot and open a GitHub draft",
|
||||
"Report copied. Keep this text to submit on GitHub when you are online.": "Report copied. Keep this text to submit on GitHub when you are online.",
|
||||
@@ -789,6 +791,7 @@
|
||||
"browser.edit_canvas_title": "Edit Canvas: {name}",
|
||||
"browser.edit_point_cloud_title": "Edit Point Cloud: {name}",
|
||||
"browser.move_title": "Move: {name}",
|
||||
"browser.needs_repair": "{name}: needs repair",
|
||||
"browser.parameters": "Parameters ({count})",
|
||||
"browser.precision": "Precision: {precision}",
|
||||
"browser.sketch_attachment_lost": "{name} (attachment lost)",
|
||||
@@ -833,6 +836,7 @@
|
||||
"cmd.assembly_mass_properties": "Mass Properties",
|
||||
"cmd.body.print_face": "Print Face",
|
||||
"cmd.body_rename": "Rename Body",
|
||||
"cmd.body_repair": "Repair Part",
|
||||
"cmd.browser_collapse_all": "Collapse All",
|
||||
"cmd.browser_expand_all": "Expand All",
|
||||
"cmd.browser_group": "New Group",
|
||||
@@ -917,6 +921,8 @@
|
||||
"cmd.diagnostics_crash_reports": "Crash Reports",
|
||||
"cmd.diagnostics_crash_reports_clear": "Delete Crash Reports",
|
||||
"cmd.diagnostics_crash_test": "Crash Test",
|
||||
"cmd.diagnostics_profile": "Profile",
|
||||
"cmd.diagnostics_validation": "Validation",
|
||||
"cmd.doc_recover": "Recover Design",
|
||||
"cmd.doc_recovery_discard": "Discard Recovered Design",
|
||||
"cmd.doc_recovery_list": "Recoverable Designs",
|
||||
@@ -954,6 +960,7 @@
|
||||
"cmd.frame_profile_list": "Profile Library",
|
||||
"cmd.frame_profile_properties": "Profile Properties",
|
||||
"cmd.inspect_accessibility": "Accessibility Analysis",
|
||||
"cmd.inspect_analysis": "View Analysis",
|
||||
"cmd.inspect_center_of_mass": "Center of Mass",
|
||||
"cmd.inspect_clearance": "Clearance",
|
||||
"cmd.inspect_colored_components": "Enable Component Colors",
|
||||
@@ -977,6 +984,7 @@
|
||||
"cmd.joint_delete": "Delete Joint",
|
||||
"cmd.joint_drive": "Drive Joints",
|
||||
"cmd.joint_edit": "Edit Joint",
|
||||
"cmd.joint_find_limits": "Find Joint Limits",
|
||||
"cmd.joint_gear": "Gear",
|
||||
"cmd.joint_inspect": "Inspect Joint",
|
||||
"cmd.joint_limits": "Joint Limits",
|
||||
@@ -1358,6 +1366,7 @@
|
||||
"crash.unclean_explanation": "Detected on a later launch; the exact exit time was not recorded. A system shutdown or forced stop can also cause an unclean exit.",
|
||||
"delete.and_more": "… and {count} more",
|
||||
"diagnostic.empty_boolean_component": "there is no body to cut or intersect in component '{component}'; the tool may overlap '{body}' in component '{other}' — activate that component or pick the body as a target",
|
||||
"diagnostic.reselect_reference": "{feature}: the reference `{reference}` no longer exists as it was picked; reselect it",
|
||||
"dialog.bodies_count": "{count} bodies",
|
||||
"dialog.clearance_invalid": "the clearance must be a length of 0 or more",
|
||||
"dialog.compute": "Compute",
|
||||
@@ -1423,6 +1432,14 @@
|
||||
"file_load.reading_percent": "Reading the file... {percent}%",
|
||||
"file_load.rebuilding": "Rebuilding feature {done} of {total}",
|
||||
"file_load.recovering": "Recovering design",
|
||||
"file_load.step_build": "Building the model",
|
||||
"file_load.step_feature": "Feature {done} of {total}",
|
||||
"file_load.step_part": "Part {done} of {total}",
|
||||
"file_load.step_percent": "{percent}%",
|
||||
"file_load.step_read": "Reading the file",
|
||||
"file_load.step_rebuild": "Rebuilding the features",
|
||||
"file_load.step_repair": "Checking and repairing the parts",
|
||||
"file_load.step_show": "Preparing the view",
|
||||
"file_load.stopping": "Waiting for the previous operation to stop...",
|
||||
"file_load.updating_links": "Updating links",
|
||||
"file_load.waiting": "Waiting for file operation",
|
||||
@@ -1717,6 +1734,7 @@
|
||||
"help.timeline.delete": "Deletes the selected features from the timeline; deleting a sketch also deletes the features built from it. Use it to remove a step that is no longer wanted.",
|
||||
"help.timeline.dependents": "Lists the features that would be deleted with a feature, and those that would fail without it. Use it before deleting or changing a feature.",
|
||||
"help.timeline.play": "Replay the remaining features one at a time, with a half-second pause after each rebuild. Run this command again or press Escape to stop; Go to end jumps immediately.",
|
||||
"help.version": "Version {version}",
|
||||
"help.view.apply": "Turns the view to a standard direction such as front, top or iso, or to a saved named view. Use it to look at the model from a fixed angle.",
|
||||
"help.view.delete": "Deletes a saved named view. The model is not changed.",
|
||||
"help.view.fit": "Zooms and pans so the whole model, or the selection, fills the view. Use it when the model has gone out of sight.",
|
||||
@@ -1804,6 +1822,27 @@
|
||||
"home.start_hint": "Create a design, open a recent file, or explore a sample.",
|
||||
"home.whats_new": "What's new",
|
||||
"home.yesterday": "Yesterday",
|
||||
"import_report.details": "Details:",
|
||||
"import_report.file_one": "There was 1 other problem with the file.",
|
||||
"import_report.file_other": "There were {count} other problems with the file.",
|
||||
"import_report.hint": "Exporting these parts again from the program that made them may fix this. Copy Warning Message has the details.",
|
||||
"import_report.left_out_one": "1 face couldn't be repaired and is missing.",
|
||||
"import_report.left_out_other": "{count} faces couldn't be repaired and are missing.",
|
||||
"import_report.needs_repair_one": "1 part needs repair before it can be modelled on.",
|
||||
"import_report.needs_repair_other": "{count} parts need repair before they can be modelled on.",
|
||||
"import_report.not_imported_one": "1 part couldn't be imported.",
|
||||
"import_report.not_imported_other": "{count} parts couldn't be imported.",
|
||||
"import_report.not_shown_one": "1 face couldn't be drawn and is hidden.",
|
||||
"import_report.not_shown_other": "{count} faces couldn't be drawn and are hidden.",
|
||||
"import_report.open_one": "1 part has gaps between its faces and came in as an open body.",
|
||||
"import_report.open_other": "{count} parts have gaps between their faces and came in as open bodies.",
|
||||
"import_report.refused_one": "1 part couldn't be made into a solid and was not imported.",
|
||||
"import_report.refused_other": "{count} parts couldn't be made into solids and were not imported.",
|
||||
"import_report.repaired_one": "Small flaws were repaired in 1 part.",
|
||||
"import_report.repaired_other": "Small flaws were repaired in {count} parts.",
|
||||
"import_report.solids": "Imported {solids} of {parts} parts as solids.",
|
||||
"import_report.surfaces_one": "1 part couldn't be made into a solid and came in as surfaces.",
|
||||
"import_report.surfaces_other": "{count} parts couldn't be made into solids and came in as surfaces.",
|
||||
"install.button_tip": "Install SolidCraft as an app: it opens in its own window and works offline",
|
||||
"install.dock_1": "Choose File › Add to Dock.",
|
||||
"install.dock_2": "Click Add.",
|
||||
@@ -1828,7 +1867,18 @@
|
||||
"issue.note_save_failed": "Could not save screenshot: {error}",
|
||||
"issue.note_saved": "Screenshot saved. Attach the PNG in the GitHub draft.",
|
||||
"issue.save_screenshot": "Save screenshot…",
|
||||
"joint.find_limits": "Find Limits",
|
||||
"joint.find_limits_failed": "The search for the limits stopped with an error",
|
||||
"joint.find_limits_hint": "Fill in the limits from where the parts this joint moves first touch another part, turning or sliding each way from where it is now. It runs on a copy: nothing changes until OK.",
|
||||
"joint.find_limits_stale": "The joint or the design has changed since these limits were found: find them again",
|
||||
"joint.finding_limits": "Finding limits…",
|
||||
"joint.from_bodies_hint": "Each picked body moves into a new component of its own and stays where it is.",
|
||||
"joint.limit_contact": "{side} {value}: {a} touches {b}",
|
||||
"joint.limit_limit": "{side} {value}: the limit of another value or joint",
|
||||
"joint.limit_loop": "{side} {value}: a closed loop cannot close past it",
|
||||
"joint.limit_range": "{side}: no contact within {range}, left as it was",
|
||||
"joint.limit_turn": "{side} {value}: no contact within one turn",
|
||||
"joint.limits_overlapping": "Already in each other here: {pairs}. They stop the search only if they go deeper.",
|
||||
"joint.one_body": "Both picks are on {body}. Pick the second one on another part.",
|
||||
"joint.one_component": "These parts are bodies of one component: {bodies}. A joint moves one component against another, so each part needs a component of its own.",
|
||||
"joint.rigid_group_one_component": "These {count} bodies are all in one component (or in the design itself): use {command} first.",
|
||||
@@ -2091,12 +2141,19 @@
|
||||
"parts.family.button_head_cap_screw": "Button head cap screw",
|
||||
"parts.family.dowel_pin": "Parallel pin (dowel)",
|
||||
"parts.family.hex_bolt": "Hex head bolt / screw",
|
||||
"parts.family.hex_cap_screw_inch": "Hex cap screw (inch)",
|
||||
"parts.family.hex_nut": "Hex nut",
|
||||
"parts.family.hex_nut_inch": "Hex nut (inch)",
|
||||
"parts.family.set_screw": "Hex socket set screw, flat point",
|
||||
"parts.family.socket_head_cap_screw": "Socket head cap screw",
|
||||
"parts.family.washer": "Plain washer",
|
||||
"parts.family.washer_sae": "Plain washer, SAE (narrow)",
|
||||
"parts.family.washer_uss": "Plain washer, USS (wide)",
|
||||
"parts.holes": "Holes (optional)",
|
||||
"parts.placement_hint": "pick one or more holes to seat a part in each, or OK places it at the origin",
|
||||
"parts.system": "Unit system",
|
||||
"parts.system.inch": "Inch",
|
||||
"parts.system.metric": "Metric",
|
||||
"pattern.alignment_2": "Alignment 2",
|
||||
"pattern.alignment_hint": "Counts include the original. Center balances copies; Flip chooses the extra side for even totals.",
|
||||
"pattern.direction_1": "Direction 1",
|
||||
@@ -2135,6 +2192,18 @@
|
||||
"prefs.alt_frees_manipulators": "Alt / Option also frees manipulator arrows",
|
||||
"prefs.anti_aliasing_amd_dx12": "Anti-aliasing uses 1× because the startup check drew nothing with multisampling and passed at 1×. You can choose another setting above; restart to apply it if the check passes.",
|
||||
"prefs.anti_aliasing_note": "Smooths jagged edges.",
|
||||
"prefs.appearance.colour_vision_help": "Changes the colors that carry a meaning: sketch states, axes, bends, analyses and icons.",
|
||||
"prefs.appearance.desktop_only": "Desktop only",
|
||||
"prefs.appearance.follow_help": "Reloads when another tool rewrites the file.",
|
||||
"prefs.appearance.interface_scale": "Interface scale",
|
||||
"prefs.appearance.next_themes": "Next themes",
|
||||
"prefs.appearance.option_count": "{count} options",
|
||||
"prefs.appearance.option_count_one": "{count} option",
|
||||
"prefs.appearance.previous_themes": "Previous themes",
|
||||
"prefs.appearance.theme_count": "{count} themes",
|
||||
"prefs.appearance.theme_own": "Theme's own",
|
||||
"prefs.appearance.viewport_colours": "3D view colors",
|
||||
"prefs.appearance.viewport_logo": "Viewport and logo",
|
||||
"prefs.applies_on_restart": "Applies on restart",
|
||||
"prefs.assistant": "Assistant",
|
||||
"prefs.auto_operation": "Choose the operation automatically",
|
||||
@@ -2149,6 +2218,8 @@
|
||||
"prefs.bottom_centre": "Bottom center",
|
||||
"prefs.bottom_right": "Bottom right",
|
||||
"prefs.browser": "Browser",
|
||||
"prefs.browser_components_open": "Components start open",
|
||||
"prefs.browser_components_open_note": "Every component in the browser starts open, as before, instead of closed until you open it.",
|
||||
"prefs.browser_eye_left": "Eye next to the fold arrow",
|
||||
"prefs.browser_eye_left_note": "Browser rows show their show/hide eye next to the fold arrow, as before, instead of in a column at the right end of the rows.",
|
||||
"prefs.browser_reveal_on_hover": "Reveal on hover",
|
||||
@@ -2198,7 +2269,19 @@
|
||||
"prefs.font_sample": "The quick brown fox · Ø12.50 mm · 45.0° · 0123456789",
|
||||
"prefs.font_unreadable": "The selected font could not be read.",
|
||||
"prefs.general": "General",
|
||||
"prefs.general_note": "Language, saving and updates.",
|
||||
"prefs.general.active": "Active",
|
||||
"prefs.general.crash_reports": "Crash reports",
|
||||
"prefs.general.edited": "Edited",
|
||||
"prefs.general.held": "Held",
|
||||
"prefs.general.import": "Import...",
|
||||
"prefs.general.last_check": "Last checked: {time}",
|
||||
"prefs.general.minutes": "Minutes",
|
||||
"prefs.general.name_setup": "Name this setup",
|
||||
"prefs.general.profile_name": "Profile name",
|
||||
"prefs.general.save_new": "Save as new",
|
||||
"prefs.general.step_import": "STEP and import",
|
||||
"prefs.general.this_computer": "This computer",
|
||||
"prefs.general_note": "Profiles, language, files and updates.",
|
||||
"prefs.gesture_orbit": "Orbit",
|
||||
"prefs.gesture_pan": "Pan",
|
||||
"prefs.gesture_zoom": "Zoom",
|
||||
@@ -2255,10 +2338,10 @@
|
||||
"prefs.logo_accent": "Accent",
|
||||
"prefs.logo_theme": "Theme's colors",
|
||||
"prefs.max_file_size": "Largest file to open",
|
||||
"prefs.max_file_size_design": "Design files",
|
||||
"prefs.max_file_size_design": "Largest file to open: Design",
|
||||
"prefs.max_file_size_flag": "This run started with --max-file-size {size}, which applies until SolidCraft closes. These settings apply when it starts without it.",
|
||||
"prefs.max_file_size_note": "Defaults: 512 MB and 256 MB; at most 16 GB. Opening a file takes several times its size in memory. A design that keeps a large import may need a higher design file limit to save and reopen.",
|
||||
"prefs.max_file_size_step": "STEP and import files",
|
||||
"prefs.max_file_size_step": "Largest file to open: STEP and import",
|
||||
"prefs.max_file_size_web": "The web app opens STEP and import files up to 512 MB and designs up to 256 MB: a browser tab can't hold larger files. The desktop app can raise these limits.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "Large icons",
|
||||
@@ -2275,7 +2358,7 @@
|
||||
"prefs.navigator_fit_note": "A face, corner or arrow of the view cube, or an axis of the gizmo, also looks at the middle of the model from the one distance that fits it from every side (its bounding sphere). Home uses the same zoom and centre while this is on, so views chosen after it only turn. Off: the view turns in place, keeping its target and zoom.",
|
||||
"prefs.network_host_remove": "Remove",
|
||||
"prefs.network_locations": "Allow network locations in designs",
|
||||
"prefs.network_locations_note": "Off: a design's linked files on another computer (\\\\server\\share) are opened only from the servers listed here, or after you allow one when asked.",
|
||||
"prefs.network_locations_note": "For organizations that keep designs on file servers. Off: only servers you allow.",
|
||||
"prefs.network_locations_section": "Network locations",
|
||||
"prefs.not_on_gpu": "not on this GPU",
|
||||
"prefs.on": "On",
|
||||
@@ -2301,10 +2384,12 @@
|
||||
"prefs.profile_applied": "Profile applied.",
|
||||
"prefs.profile_changed": "Settings changed since the profile was saved. Save keeps them in it.",
|
||||
"prefs.profile_deleted": "Profile deleted.",
|
||||
"prefs.profile_export": "Export…",
|
||||
"prefs.profile_export": "Export",
|
||||
"prefs.profile_exported": "Profile exported.",
|
||||
"prefs.profile_imported": "Profile imported. Choose it under Active profile to switch to it.",
|
||||
"prefs.profile_imported": "Profile imported.",
|
||||
"prefs.profile_name_hint": "Profile name, e.g. Laptop",
|
||||
"prefs.profile_operations": "Profile operations",
|
||||
"prefs.profile_operations_note": "Records how long SMLib calls, kernel operations, features, commands and imports take. Agents read it with diagnostics.profile.",
|
||||
"prefs.profile_rename": "Rename",
|
||||
"prefs.profile_renamed": "Profile renamed.",
|
||||
"prefs.profile_save": "Save",
|
||||
@@ -2323,16 +2408,20 @@
|
||||
"prefs.render_previous_geometry_budget": "Use previous Render preview geometry budget",
|
||||
"prefs.render_previous_geometry_budget_help": "Restore the previous fixed geometry limit (512 MiB on 64-bit desktop, 128 MiB elsewhere) instead of the device-based budget.",
|
||||
"prefs.reports_saved": "{n} saved",
|
||||
"prefs.restore_page_defaults": "Restore page defaults",
|
||||
"prefs.ribbon_click_ends_tool": "Clicking the running tool's button ends it",
|
||||
"prefs.ribbon_click_ends_tool_note": "A click on the lit ribbon button of the sketch tool in use ends the tool, so clicks in the view select again. Off: the click starts the tool again.",
|
||||
"prefs.ribbon_reveal": "Show search results in the ribbon",
|
||||
"prefs.ribbon_reveal_note": "Hovering a result in Search commands shows where the command sits in the ribbon: its tab, its button or its row in a panel's drop-down. A command started from the search blinks there, and a running command from a drop-down marks its panel.",
|
||||
"prefs.right_click": "Right click",
|
||||
"prefs.saving": "Autosave and recovery",
|
||||
"prefs.saving": "Start and save",
|
||||
"prefs.scale_note": "Also Ctrl + / Ctrl − in the window. Buttons are 28 px at 100%.",
|
||||
"prefs.scale_text": "Scale and text",
|
||||
"prefs.search": "Search preferences",
|
||||
"prefs.search_empty": "No matching preferences",
|
||||
"prefs.section_hatch": "Section hatch",
|
||||
"prefs.section_hatch_by_part": "By part",
|
||||
"prefs.section_hatch_one": "One style",
|
||||
"prefs.select": "Select",
|
||||
"prefs.select_one_item": "Select one item",
|
||||
"prefs.select_other_hidden": "Select hidden geometry with Select Other",
|
||||
@@ -2447,6 +2536,12 @@
|
||||
"prefs.under_cursor": "Under cursor",
|
||||
"prefs.units_precision": "Units and precision",
|
||||
"prefs.updates": "Updates",
|
||||
"prefs.validation": "Body check",
|
||||
"prefs.validation.each_feature": "Once per feature",
|
||||
"prefs.validation.each_step": "After every step",
|
||||
"prefs.validation.label": "Full kernel check",
|
||||
"prefs.validation.off": "Off (experimental)",
|
||||
"prefs.validation_note": "After every step: checks each sweep, boolean and blend.\nOnce per feature: checks each feature's result once.\nOff (experimental): no check.",
|
||||
"prefs.value_boxes_in_view": "Show values beside their handles in the view",
|
||||
"prefs.value_boxes_in_view_tip": "A command's value (Distance, Radius…) in a box next to its handle, over the model. Off: you type it in the command window, which takes the keyboard after a pick, and nothing covers the model.",
|
||||
"prefs.view": "View behavior",
|
||||
@@ -2481,6 +2576,17 @@
|
||||
"recovery.just_now": "just now",
|
||||
"recovery.min_ago": "{count} min ago",
|
||||
"recovery.never_saved": "never saved",
|
||||
"refused.change": "Couldn't change {body}.",
|
||||
"refused.cut": "Couldn't cut {body}.",
|
||||
"refused.face_curved": "One of its curved faces can't be used for modelling.",
|
||||
"refused.face_flat": "One of its flat faces can't be used for modelling.",
|
||||
"refused.face_freeform": "One of its freeform faces can't be used for modelling.",
|
||||
"refused.face_rounded": "One of its rounded faces can't be used for modelling.",
|
||||
"refused.faces_apart": "Its faces don't meet closely enough to be used for modelling.",
|
||||
"refused.intersect": "Couldn't intersect with {body}.",
|
||||
"refused.join": "Couldn't join to {body}.",
|
||||
"refused.repair": "Repair part",
|
||||
"refused.show_face": "Show face",
|
||||
"render.adaptive_absolute": "Absolute RGB error floor",
|
||||
"render.adaptive_coverage": "Coverage error target",
|
||||
"render.adaptive_enable": "Allocate samples with independent pilots",
|
||||
@@ -3948,11 +4054,20 @@
|
||||
"the section's profiles must be in one sketch": "the section's profiles must be in one sketch",
|
||||
"the two origins are on the same component": "the two origins are on the same component",
|
||||
"thread.automatic_size": "Automatic size",
|
||||
"thread.chamfer_depth_needed": "required for this form",
|
||||
"thread.designation": "Designation",
|
||||
"thread.direction": "Thread direction",
|
||||
"thread.drill_range": "The tap maker's drill is {drill} mm. The internal thread's standard minor diameter is {minor} mm. A drill from {low} to {high} mm is accepted.",
|
||||
"thread.drill_range_custom": "The drill is the major diameter less the pitch, {drill} mm. The internal thread's standard minor diameter is {minor} mm. A drill from {low} to {high} mm is accepted.",
|
||||
"thread.drill_range_taper": "The tap maker's drill is {drill} mm. The standard's minor diameter where the thread ends is {minor} mm. A drill from {low} to {high} mm is accepted.",
|
||||
"thread.end_square": "Square",
|
||||
"thread.fade_short": "1.25 P (ISO 3508)",
|
||||
"thread.fade_standard": "2.5 P (ISO 3508)",
|
||||
"thread.fits_the_face": "fits the face, e.g. {example}",
|
||||
"thread.form_bspp": "Pipe G (ISO 228-1)",
|
||||
"thread.form_bspt": "Pipe taper R, Rc (ISO 7-1)",
|
||||
"thread.form_npt": "Pipe taper NPT (ASME B1.20.1)",
|
||||
"thread.form_rp": "Pipe Rp (ISO 7-1)",
|
||||
"thread.iso_metric": "ISO metric",
|
||||
"thread.left_hand": "Left-hand",
|
||||
"thread.right_hand": "Right-hand",
|
||||
"thread.select_size": "Select size",
|
||||
"timeline.delete_after_marker_one": "Delete 1 Feature After History Marker",
|
||||
"timeline.delete_after_marker_other": "Delete {count} Features After History Marker",
|
||||
@@ -4126,6 +4241,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Run this in a terminal, then press Connect again:",
|
||||
"ui.agent_chat_auth_title": "Sign in to the agent",
|
||||
"ui.agent_chat_cancel": "Cancel",
|
||||
"ui.agent_chat_cancel_timeout": "The agent did not respond to Stop and was disconnected. Reconnect to continue.",
|
||||
"ui.agent_chat_cannot_start": "Installed, but it does not start: {error}",
|
||||
"ui.agent_chat_changes": "Changes: {n}, one undo step",
|
||||
"ui.agent_chat_changes_n": "{n} changes",
|
||||
@@ -4203,6 +4319,7 @@
|
||||
"ui.agent_chat_no_images": "This agent doesn't take pictures",
|
||||
"ui.agent_chat_no_picture": "There is no picture on the clipboard",
|
||||
"ui.agent_chat_no_tool_call": "The model didn't use SolidCraft's tools, so the design wasn't changed. Choose a model that supports tool calling; see Local models in the agent chat guide.",
|
||||
"ui.agent_chat_node_too_old": "Node.js {version} is too old for this agent. Install Node.js 20.10 or newer, then press Refresh.",
|
||||
"ui.agent_chat_none_found": "No coding agent found. Install one of these, then press Refresh:",
|
||||
"ui.agent_chat_none_found_short": "No agent found",
|
||||
"ui.agent_chat_not_installed": "Not installed",
|
||||
@@ -4422,6 +4539,8 @@
|
||||
"ui.centerline": "Centerline",
|
||||
"ui.chain": "Chain",
|
||||
"ui.chamfer": "Chamfer",
|
||||
"ui.chamfer_angle": "Chamfer Angle",
|
||||
"ui.chamfer_depth": "Chamfer Depth",
|
||||
"ui.chamfer_type": "Chamfer Type",
|
||||
"ui.change_workspace": "Change workspace",
|
||||
"ui.circle": "Circle",
|
||||
@@ -4452,10 +4571,16 @@
|
||||
"ui.colour_vision_sample": "Sample",
|
||||
"ui.colour_vision_tritan": "Tritan",
|
||||
"ui.colour_vision_tritan_tip": "For a weak or missing blue sense",
|
||||
"ui.comb_length": "Comb length",
|
||||
"ui.comb_length_tip": "How long the teeth are: a multiple of the automatic length, which makes the longest tooth a fifth of the size of the curves shown.",
|
||||
"ui.comb_teeth": "Teeth",
|
||||
"ui.comb_teeth_tip": "How many teeth each comb has, evenly spaced along the curve. More teeth show more detail of how the curvature changes.",
|
||||
"ui.combine": "Combine",
|
||||
"ui.coming_later": "coming later",
|
||||
"ui.command_done": "{command} done",
|
||||
"ui.command_palette_hint": "Type a command, e.g. {example} — or press S to search",
|
||||
"ui.components_from_bodies_hint": "Create one component per selected body, named after that body, under the active component.",
|
||||
"ui.components_from_bodies_required": "Select at least one body.",
|
||||
"ui.concentric": "Concentric",
|
||||
"ui.cone": "Cone",
|
||||
"ui.conic_curve": "Conic Curve",
|
||||
@@ -4474,11 +4599,24 @@
|
||||
"ui.construction.ribbon_label": "X-Lines",
|
||||
"ui.construction.side_hint": "Construction line: click the side the line runs towards; right-click to restart",
|
||||
"ui.context_menu": "Context menu",
|
||||
"ui.continuity_crease": "Crease",
|
||||
"ui.continuity_curvature": "Curvature",
|
||||
"ui.continuity_curvature_change": "Curvature change",
|
||||
"ui.continuity_curvature_change_tip": "Curvature change (G3): the curvature also changes smoothly across the seam.",
|
||||
"ui.continuity_curvature_tip": "Curvature-continuous (G2): reflections run through without a kink.",
|
||||
"ui.continuity_g0": "G0 crease",
|
||||
"ui.continuity_g1": "G1 tangent",
|
||||
"ui.continuity_g2": "G2 curvature",
|
||||
"ui.continuity_gap": "Gap",
|
||||
"ui.continuity_position": "Position",
|
||||
"ui.continuity_position_tip": "Position (G0): the surfaces meet, with a crease where they do.",
|
||||
"ui.continuity_smooth_curvature_change": "Smooth curvature change",
|
||||
"ui.continuity_smooth_curvature_change_tip": "Smooth curvature change (G4): even the way the curvature changes runs on smoothly across the seam.",
|
||||
"ui.continuity_tangent": "Tangent",
|
||||
"ui.continuity_tangent_tip": "Tangent (G1): no crease, though the curvature jumps across the seam.",
|
||||
"ui.copy": "Copy",
|
||||
"ui.copy_details": "Copy Details",
|
||||
"ui.copy_details_tip": "Copy the full message, to look it up or to report it.",
|
||||
"ui.corner": "Corner",
|
||||
"ui.corners": "Corners",
|
||||
"ui.cosmetic_thread_markings": "Cosmetic thread markings",
|
||||
@@ -4596,8 +4734,10 @@
|
||||
"ui.edit_sketch_empty": "No sketches to edit",
|
||||
"ui.ellipse": "Ellipse",
|
||||
"ui.emboss_wrap_note": "Orthogonal line profiles tangent to one supported cylinder wrap automatically. Pick a wall to choose a target.",
|
||||
"ui.end": "End",
|
||||
"ui.end_radius": "End Radius",
|
||||
"ui.end_scale": "End Scale",
|
||||
"ui.ends": "Ends",
|
||||
"ui.entities": "Entities",
|
||||
"ui.equal": "Equal",
|
||||
"ui.equation_end": "End",
|
||||
@@ -4617,6 +4757,7 @@
|
||||
"ui.export_ellipsis_keymap": "Export…",
|
||||
"ui.export_keymap_tip": "Keys, S box favorites and the toolbar, as a file",
|
||||
"ui.extend": "Extend",
|
||||
"ui.extent": "Extent",
|
||||
"ui.extent_type": "Extent Type",
|
||||
"ui.extract_component": "Extract to File…",
|
||||
"ui.extract_design_changed": "The design changed while choosing a file. Start Extract to File again.",
|
||||
@@ -4625,11 +4766,39 @@
|
||||
"ui.extrude_auto_to_object": "Extrude to a picked target",
|
||||
"ui.extrude_auto_to_object_tip": "In Extrude, with a profile picked, picking a face, vertex or parallel plane off its plane switches the extent to To Object",
|
||||
"ui.factor": "Factor",
|
||||
"ui.fade": "Fade",
|
||||
"ui.fade_length": "Fade Length",
|
||||
"ui.feature_history": "Feature history",
|
||||
"ui.feature_options": "Feature Options",
|
||||
"ui.features": "Features",
|
||||
"ui.file": "File",
|
||||
"ui.fillet": "Fillet",
|
||||
"ui.fillet_corner_ball": "Ball",
|
||||
"ui.fillet_corner_ball_tip": "A piece of the rolling ball's sphere, tangent (G1) to each round.",
|
||||
"ui.fillet_corner_curvature_tip": "Curvature (G2): a smooth patch. The corner is set back along each edge by the Setback ({factor} × the radius unless you type one) and filled with a patch, so reflections run through it without a kink.",
|
||||
"ui.fillet_corner_needs_constant": "Smooth patch corners (G2) need Constant radius and Exact fit.",
|
||||
"ui.fillet_corner_patch": "Smooth patch",
|
||||
"ui.fillet_corner_patch_tip": "Curvature-continuous (G2): the corner is set back along each edge and filled with a patch, so reflections run through it without a kink.",
|
||||
"ui.fillet_corner_tangent_tip": "Tangent (G1): a rolling-ball corner, a piece of the rolling ball's sphere tangent to each round.",
|
||||
"ui.fillet_error_corner": "The smooth corner could not be built here. Try another Setback, or Tangent corners.",
|
||||
"ui.fillet_error_failed": "This fillet could not be built on these edges. Try another radius, or round fewer edges at once.",
|
||||
"ui.fillet_error_setback_short": "The setback is too short for this corner's patch: it builds from {least} on. Try a longer Setback.",
|
||||
"ui.fillet_error_too_large": "The radius is too large for the faces beside these edges. Try a smaller radius, or set Face limits to Largest radius.",
|
||||
"ui.fillet_fit": "Face limits",
|
||||
"ui.fillet_fit_exact": "Exact radius",
|
||||
"ui.fillet_fit_needs_constant": "Radius fit needs Constant radius.",
|
||||
"ui.fillet_fit_reduce": "Largest radius",
|
||||
"ui.fillet_fit_taper": "Narrowed radius",
|
||||
"ui.fillet_fit_tip": "When the radius is larger than the faces beside an edge allow.\nExact radius: the radius as given, or nothing rounded and a message saying why.\nLargest radius: every edge rounded with the largest radius that fits.\nNarrowed radius: the radius kept, the round narrowed only where the faces run out.",
|
||||
"ui.fillet_options_tip": "Tangent chain: also round the edges that continue smoothly from the picked ones.\nPreview: show the result in the view while you edit.",
|
||||
"ui.fillet_setback": "Setback",
|
||||
"ui.fillet_setback_hint": "{factor} × radius",
|
||||
"ui.fillet_setback_least": "More than {least}",
|
||||
"ui.fillet_setback_tip": "Where the smooth corner patch starts: how far along each edge from the corner. The default is {factor} × the radius. It must be more than the least setback, where the rounds meet without one: the radius where the faces are square to each other.",
|
||||
"ui.fillet_smooth_radius": "Smooth radius change",
|
||||
"ui.fillet_type_choices_tip": "Constant radius: one radius all along the edge.\nChord length: the width across the round stays the same, whatever angle the faces meet at.\nVariable radius: a radius at each end, and more along the edge if you add them.\nCurvature continuous: a section without a kink in reflections where it meets flat faces (G2).\nSpline: the radius along the edge follows control radii you set.",
|
||||
"ui.fillet_type_curvature": "Curvature continuous",
|
||||
"ui.fillet_type_tip": "The section across the edge. Curvature continuous: a curvature-continuous (G2) section between planar faces, so reflections run from the faces into the round without a kink.",
|
||||
"ui.filter_commands_or_keys": "Filter commands or keys",
|
||||
"ui.findInTimeline": "Find in Timeline",
|
||||
"ui.findInWindow": "Find in Window",
|
||||
@@ -4761,6 +4930,7 @@
|
||||
"ui.history_sketch_listed_under": "Listed under {feature}",
|
||||
"ui.history_title": "History",
|
||||
"ui.history_toggle_names": "Show or hide feature names",
|
||||
"ui.history_waits_for_editor": "Undo, Redo and History wait until the feature editor has opened",
|
||||
"ui.hole": "Hole",
|
||||
"ui.hole_tap_type": "Hole Tap Type",
|
||||
"ui.hole_type": "Hole Type",
|
||||
@@ -4778,6 +4948,7 @@
|
||||
"ui.inspect": "Inspect",
|
||||
"ui.instances": "Instances",
|
||||
"ui.intersect": "Intersect",
|
||||
"ui.iso_4753": "ISO 4753",
|
||||
"ui.isolate": "Isolate",
|
||||
"ui.join": "Join",
|
||||
"ui.joint_play": "▶ Play",
|
||||
@@ -4880,6 +5051,7 @@
|
||||
"ui.part_size_needs_a_part": "Select an inserted standard part (a screw, nut, washer or pin) to change its size. To make a body larger or smaller, use Scale.",
|
||||
"ui.part_size_shared": "Rebuilds the part, replacing any added features. All occurrences of this component update.",
|
||||
"ui.paste": "Paste",
|
||||
"ui.patch_continuity_corner_tip": "The patch meets the faces round it at the chosen continuity along each side. At a corner where those faces meet each other with different curvatures, it stays tangent (G1) there.",
|
||||
"ui.path": "Path",
|
||||
"ui.pattern_object_type_tip": "Bodies: solid shapes, picked in the view or the Browser; each is copied whole.\nFeatures: steps of the History, such as an extrude or a hole, picked in the History or on a face they made; the step is repeated.\nA sketch is neither: repeat its curves with the sketch's own Pattern or Mirror, or extrude it first.",
|
||||
"ui.pattern_on_path": "Pattern on Path",
|
||||
@@ -5056,6 +5228,8 @@
|
||||
"ui.sketch_intersect_geometry": "Geometry",
|
||||
"ui.sketch_intersect_projection_link": "Projection Link",
|
||||
"ui.sketch_intersect_selection_filter": "Selection Filter",
|
||||
"ui.sketch_keys_only": "Keep model keys out of sketches",
|
||||
"ui.sketch_keys_only_tip": "While editing a sketch, only sketch keys and keys that work everywhere run; a model key such as E or F does nothing, so a stray key can't close a heavy sketch.",
|
||||
"ui.sketch_modifiers": "Sketch modifiers",
|
||||
"ui.sketch_project_geometry": "Geometry",
|
||||
"ui.sketch_report.all_constrained": "Everything is fully constrained.",
|
||||
@@ -5073,7 +5247,7 @@
|
||||
"ui.slicer_no_response": "{slicer} didn't respond in time. The file is already saved at {path}: send it again once the slicer has started.",
|
||||
"ui.slicer_resend_hint": "Sending again replaces the file the slicer has open.",
|
||||
"ui.slicer_single_instance": "Open files in a slicer that is already running",
|
||||
"ui.slicer_single_instance_tip": "PrusaSlicer, SuperSlicer and Cura load the file in the window that is open instead of starting another. Other slicers are started as they are. PrusaSlicer 3.0 always does this: its launcher can't take a file any other way.",
|
||||
"ui.slicer_single_instance_tip": "For slicers that load a file into a running window.",
|
||||
"ui.slicer_starting": "Starting {slicer}…",
|
||||
"ui.slicer_still_starting": "Still waiting for {slicer} to start. Nothing else was sent.",
|
||||
"ui.slicer_stl_fallback": "The 3MF could not be written, so this is an STL.",
|
||||
@@ -5218,13 +5392,13 @@
|
||||
"update.available": "SolidCraft {version} is available",
|
||||
"update.button": "Update",
|
||||
"update.button_version": "Update to {version}",
|
||||
"update.check_automatically": "Check for updates automatically",
|
||||
"update.check_automatically": "Check at start and once a day",
|
||||
"update.check_for_updates": "Check for updates",
|
||||
"update.check_now": "Check now",
|
||||
"update.check_unavailable": "Update checks are not available for this installation.",
|
||||
"update.checking": "Checking…",
|
||||
"update.copy": "Copy",
|
||||
"update.dmg_opened": "The disk image is open and verified: drag SolidCraft to Applications, replacing the old copy, then restart SolidCraft.",
|
||||
"update.dmg_opened": "The disk image is open and verified: quit SolidCraft, drag the new SolidCraft to Applications, replacing the old copy, then open SolidCraft again.",
|
||||
"update.download": "Download",
|
||||
"update.download_open": "Download and open",
|
||||
"update.downloading": "Downloading and verifying… {done} of {total} MB",
|
||||
|
||||
+15
-4
@@ -1806,12 +1806,19 @@
|
||||
"parts.family.button_head_cap_screw": "Tornillo cabeza botón",
|
||||
"parts.family.dowel_pin": "Pasador cilíndrico",
|
||||
"parts.family.hex_bolt": "Tornillo de cabeza hexagonal",
|
||||
"parts.family.hex_cap_screw_inch": "Tornillo de cabeza hexagonal (pulgadas)",
|
||||
"parts.family.hex_nut": "Tuerca hexagonal",
|
||||
"parts.family.hex_nut_inch": "Tuerca hexagonal (pulgadas)",
|
||||
"parts.family.set_screw": "Prisionero con hexágono interior, punta plana",
|
||||
"parts.family.socket_head_cap_screw": "Tornillo cilíndrico con hexágono interior",
|
||||
"parts.family.washer": "Arandela plana",
|
||||
"parts.family.washer_sae": "Arandela plana, SAE (estrecha)",
|
||||
"parts.family.washer_uss": "Arandela plana, USS (ancha)",
|
||||
"parts.holes": "Agujeros (opcional)",
|
||||
"parts.placement_hint": "elige uno o más agujeros para colocar una pieza en cada uno, o Aceptar la coloca en el origen",
|
||||
"parts.system": "Sistema de unidades",
|
||||
"parts.system.inch": "Pulgadas",
|
||||
"parts.system.metric": "Métrico",
|
||||
"pattern.alignment_2": "Alineación 2",
|
||||
"pattern.alignment_hint": "Los recuentos incluyen el original. Centrar equilibra las copias; Invertir elige el lado adicional cuando el total es par.",
|
||||
"pattern.direction_1": "Dirección 1",
|
||||
@@ -1864,6 +1871,8 @@
|
||||
"prefs.bottom_centre": "Centro abajo",
|
||||
"prefs.bottom_right": "Derecha abajo",
|
||||
"prefs.browser": "Explorador",
|
||||
"prefs.browser_components_open": "Componentes abiertos al inicio",
|
||||
"prefs.browser_components_open_note": "Cada componente del explorador empieza abierto, como antes, en lugar de cerrado hasta que lo abras.",
|
||||
"prefs.browser_eye_left": "Ojo junto a la flecha de plegado",
|
||||
"prefs.browser_eye_left_note": "Las filas del explorador muestran su ojo de mostrar/ocultar junto a la flecha de plegado, como antes, en lugar de",
|
||||
"prefs.browser_reveal_on_hover": "Mostrar al pasar el puntero",
|
||||
@@ -3590,10 +3599,7 @@
|
||||
"the two origins are on the same component": "los dos orígenes están en el mismo componente",
|
||||
"thread.automatic_size": "Tamaño automático",
|
||||
"thread.designation": "Designación",
|
||||
"thread.direction": "Dirección de la rosca",
|
||||
"thread.iso_metric": "Métrica ISO",
|
||||
"thread.left_hand": "Zurda",
|
||||
"thread.right_hand": "Diestra",
|
||||
"thread.select_size": "Seleccionar tamaño",
|
||||
"timeline.delete_after_marker_one": "Eliminar 1 operación después del marcador de la línea de tiempo",
|
||||
"timeline.delete_after_marker_other": "Eliminar {count} operaciones después del marcador de la línea de tiempo",
|
||||
@@ -4098,10 +4104,15 @@
|
||||
"ui.construction.reference_hint": "Línea de construcción: elige una línea recta, luego un punto; clic derecho para cambiar la",
|
||||
"ui.construction.ribbon_label": "X-Líneas",
|
||||
"ui.context_menu": "Menú contextual",
|
||||
"ui.continuity_crease": "Arista",
|
||||
"ui.continuity_curvature": "Curvatura",
|
||||
"ui.continuity_curvature_change": "Cambio de curvatura",
|
||||
"ui.continuity_g0": "G0 arista",
|
||||
"ui.continuity_g1": "G1 tangencia",
|
||||
"ui.continuity_g2": "G2 curvatura",
|
||||
"ui.continuity_gap": "Separación",
|
||||
"ui.continuity_position": "Posición",
|
||||
"ui.continuity_tangent": "Tangente",
|
||||
"ui.copy": "Copiar",
|
||||
"ui.corner": "Esquina",
|
||||
"ui.corners": "Esquinas",
|
||||
@@ -4799,7 +4810,7 @@
|
||||
"update.check_unavailable": "Las comprobaciones de actualización no están disponibles para esta instalación.",
|
||||
"update.checking": "Comprobando…",
|
||||
"update.copy": "Copiar",
|
||||
"update.dmg_opened": "La imagen de disco está abierta y verificada: arrastra SolidCraft a Aplicaciones, sustituye la copia anterior y reinicia SolidCraft.",
|
||||
"update.dmg_opened": "La imagen de disco está abierta y verificada: sal de SolidCraft, arrastra el nuevo SolidCraft a Aplicaciones, sustituye la copia anterior y vuelve a abrir SolidCraft.",
|
||||
"update.download": "Descargar",
|
||||
"update.download_open": "Descargar y abrir",
|
||||
"update.downloading": "Descargando y verificando… {done} de {total} MB",
|
||||
|
||||
@@ -3564,10 +3564,7 @@
|
||||
"the two origins are on the same component": "molemmat origot ovat samassa komponentissa",
|
||||
"thread.automatic_size": "Automaattinen koko",
|
||||
"thread.designation": "Merkintä",
|
||||
"thread.direction": "Kierteen suunta",
|
||||
"thread.iso_metric": "ISO-metrinen",
|
||||
"thread.left_hand": "Vasenkätinen",
|
||||
"thread.right_hand": "Oikeakätinen",
|
||||
"thread.select_size": "Valitse koko",
|
||||
"timeline.delete_after_marker_one": "Poista 1 piirre historiamerkin jälkeen",
|
||||
"timeline.delete_after_marker_other": "Poista {count} piirrettä historiamerkin jälkeen",
|
||||
|
||||
+15
-4
@@ -2054,12 +2054,19 @@
|
||||
"parts.family.button_head_cap_screw": "Vis à tête bombée à six pans creux",
|
||||
"parts.family.dowel_pin": "Goupille cylindrique",
|
||||
"parts.family.hex_bolt": "Vis à tête hexagonale",
|
||||
"parts.family.hex_cap_screw_inch": "Vis à tête hexagonale (pouces)",
|
||||
"parts.family.hex_nut": "Écrou hexagonal",
|
||||
"parts.family.hex_nut_inch": "Écrou hexagonal (pouces)",
|
||||
"parts.family.set_screw": "Vis sans tête à six pans creux, bout plat",
|
||||
"parts.family.socket_head_cap_screw": "Vis à tête cylindrique à six pans creux",
|
||||
"parts.family.washer": "Rondelle plate",
|
||||
"parts.family.washer_sae": "Rondelle plate, SAE (étroite)",
|
||||
"parts.family.washer_uss": "Rondelle plate, USS (large)",
|
||||
"parts.holes": "Trous (facultatif)",
|
||||
"parts.placement_hint": "choisissez un ou plusieurs trous pour y loger une pièce chacun, ou OK la place à l'origine",
|
||||
"parts.system": "Système d'unités",
|
||||
"parts.system.inch": "Pouces",
|
||||
"parts.system.metric": "Métrique",
|
||||
"pattern.alignment_2": "Alignement 2",
|
||||
"pattern.alignment_hint": "La quantité inclut l’original. Le centrage répartit les copies de chaque côté ; si la quantité est paire, l’inversion choisit le côté qui reçoit la copie supplémentaire.",
|
||||
"pattern.direction_1": "Direction 1",
|
||||
@@ -2112,6 +2119,8 @@
|
||||
"prefs.bottom_centre": "Bas centre",
|
||||
"prefs.bottom_right": "Bas droite",
|
||||
"prefs.browser": "Navigateur",
|
||||
"prefs.browser_components_open": "Composants dépliés au départ",
|
||||
"prefs.browser_components_open_note": "Chaque composant du navigateur commence déplié, comme avant, au lieu de rester replié jusqu'à ce que vous le dépliiez.",
|
||||
"prefs.browser_eye_left": "Œil à côté de la flèche de dépliage",
|
||||
"prefs.browser_eye_left_note": "Les lignes du navigateur affichent leur œil d'affichage et de masquage à côté de la flèche de dépliage, comme avant, au lieu d'une colonne à l'extrémité droite des lignes.",
|
||||
"prefs.browser_reveal_on_hover": "Révéler au survol",
|
||||
@@ -3853,10 +3862,7 @@
|
||||
"the two origins are on the same component": "les deux origines se trouvent sur le même composant",
|
||||
"thread.automatic_size": "Taille automatique",
|
||||
"thread.designation": "Désignation",
|
||||
"thread.direction": "Sens du filetage",
|
||||
"thread.iso_metric": "Métrique ISO",
|
||||
"thread.left_hand": "À gauche",
|
||||
"thread.right_hand": "À droite",
|
||||
"thread.select_size": "Sélectionner la taille",
|
||||
"timeline.delete_after_marker_one": "Supprimer 1 fonction après le marqueur d'historique",
|
||||
"timeline.delete_after_marker_other": "Supprimer {count} fonctions après le marqueur d'historique",
|
||||
@@ -4363,10 +4369,15 @@
|
||||
"ui.construction.reference_hint": "Droite de construction : sélectionnez une ligne droite, puis un point ; clic droit pour changer la référence",
|
||||
"ui.construction.ribbon_label": "Lignes aux.",
|
||||
"ui.context_menu": "Menu contextuel",
|
||||
"ui.continuity_crease": "Angle vif",
|
||||
"ui.continuity_curvature": "Courbure",
|
||||
"ui.continuity_curvature_change": "Variation de courbure",
|
||||
"ui.continuity_g0": "G0 angle vif",
|
||||
"ui.continuity_g1": "G1 tangence",
|
||||
"ui.continuity_g2": "G2 courbure",
|
||||
"ui.continuity_gap": "Écart",
|
||||
"ui.continuity_position": "Position",
|
||||
"ui.continuity_tangent": "Tangence",
|
||||
"ui.copy": "Copier",
|
||||
"ui.corner": "Angle",
|
||||
"ui.corners": "Angles",
|
||||
@@ -5100,7 +5111,7 @@
|
||||
"update.check_unavailable": "La recherche de mises à jour n’est pas disponible pour cette installation.",
|
||||
"update.checking": "Vérification…",
|
||||
"update.copy": "Copier",
|
||||
"update.dmg_opened": "L'image disque est ouverte et vérifiée : faites glisser SolidCraft dans Applications en remplaçant l'ancienne copie, puis redémarrez SolidCraft.",
|
||||
"update.dmg_opened": "L'image disque est ouverte et vérifiée : quittez SolidCraft, faites glisser le nouveau SolidCraft dans Applications en remplaçant l'ancienne copie, puis rouvrez SolidCraft.",
|
||||
"update.download": "Télécharger",
|
||||
"update.download_open": "Télécharger et ouvrir",
|
||||
"update.downloading": "Téléchargement et vérification… {done} sur {total} Mo",
|
||||
|
||||
+85
-4
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "Rectangular",
|
||||
"Rectangular Tube": "Tubo rectangular",
|
||||
"Reference face": "Cara de referencia",
|
||||
"Reference plane": "Plano de referencia",
|
||||
"Relief Depth": "Profundidade do desafogo",
|
||||
"Relief Width": "Largura do desafogo",
|
||||
"Remove Tools": "Quitar as ferramentas",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "Sección",
|
||||
"Section (optional)": "Sección (opcional)",
|
||||
"Section position": "Posición da sección",
|
||||
"Sections": "Seccións",
|
||||
"Sections (one sketch each)": "Seccións (un bosquexo cada unha)",
|
||||
"Select Other": "Seleccionar outro",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Seleccionar outro: mantén o botón esquerdo quieto sobre o modelo (ou clic dereito › Seleccionar outro…) para listar todo o que hai baixo o cursor; fai clic de novo no mesmo punto para seleccionar o seguinte. Fai dobre clic co botón central para ir ao Inicio (Preferencias › Navegación).",
|
||||
@@ -859,6 +861,7 @@
|
||||
"cmd.component_extract": "Extraer a un ficheiro",
|
||||
"cmd.component_finish_edit": "Rematar a edición",
|
||||
"cmd.component_from_bodies": "Crear compoñentes a partir de corpos",
|
||||
"cmd.component_hide_history": "Ocultar o historial do compoñente",
|
||||
"cmd.component_insert_design": "Inserir deseño",
|
||||
"cmd.component_links": "Listar as ligazóns",
|
||||
"cmd.component_list": "Listar os compoñentes",
|
||||
@@ -875,6 +878,7 @@
|
||||
"cmd.component_rename": "Renomear o compoñente",
|
||||
"cmd.component_replace_with_link": "Substituír por unha ligazón",
|
||||
"cmd.component_revert_position": "Reverter a posición",
|
||||
"cmd.component_show_history": "Mostrar o historial do compoñente",
|
||||
"cmd.component_unlock": "Desbloquear o contido",
|
||||
"cmd.component_update_links": "Actualizar as ligazóns",
|
||||
"cmd.config_activate": "Activar a configuración",
|
||||
@@ -885,6 +889,7 @@
|
||||
"cmd.config_rule": "Regra de configuración",
|
||||
"cmd.config_rules": "Regras de configuración",
|
||||
"cmd.config_table": "Mostrar a táboa de configuracións",
|
||||
"cmd.construct.plane.axis_point": "Plano por un eixe e un punto",
|
||||
"cmd.construct_axis_edge": "Eixe pola aresta",
|
||||
"cmd.construct_axis_normal_to_face": "Eixe perpendicular á cara",
|
||||
"cmd.construct_axis_through_cylinder": "Eixe por cilindro/cono/toro",
|
||||
@@ -933,6 +938,11 @@
|
||||
"cmd.explode_show": "Mostrar a vista explosionada",
|
||||
"cmd.expr_evaluate": "Avaliar a expresión",
|
||||
"cmd.face_delete": "Eliminar a cara",
|
||||
"cmd.file.export_3mf": "Exportar 3MF",
|
||||
"cmd.file.export_obj": "Exportar OBJ",
|
||||
"cmd.file.export_step": "Exportar STEP",
|
||||
"cmd.file.export_stl": "Exportar STL",
|
||||
"cmd.file.export_urdf": "Exportar URDF",
|
||||
"cmd.file_export": "Exportar",
|
||||
"cmd.file_insert_mesh": "Inserir malla",
|
||||
"cmd.file_insert_step": "Inserir STEP",
|
||||
@@ -950,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "Activar as cores dos compoñentes",
|
||||
"cmd.inspect_colored_components_disable": "Desactivar as cores dos compoñentes",
|
||||
"cmd.inspect_compare": "Comparar corpos",
|
||||
"cmd.inspect_compare_picture": "Comparar a imaxe",
|
||||
"cmd.inspect_continuity": "Análise de continuidade",
|
||||
"cmd.inspect_curvature_comb": "Análise con pente de curvatura",
|
||||
"cmd.inspect_curvature_map": "Análise con mapa de curvatura",
|
||||
@@ -1040,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "Editar a nube de puntos",
|
||||
"cmd.pointcloud_insert": "Inserir nube de puntos",
|
||||
"cmd.pointcloud_list": "Nubes de puntos",
|
||||
"cmd.render.compare.divider": "Mover o divisor da comparación de renders",
|
||||
"cmd.render.result.discard": "Descartar o resultado do render",
|
||||
"cmd.render.result.select": "Seleccionar o resultado do render",
|
||||
"cmd.render_aging_describe": "Envellecemento da superficie por capas",
|
||||
"cmd.render_backends": "Motores de render",
|
||||
"cmd.render_batch": "Renderizar todas as tomas",
|
||||
@@ -1254,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "Mover o marcador da liña de tempo",
|
||||
"cmd.timeline_stop": "Deter a reprodución do historial",
|
||||
"cmd.timeline_suppress": "Suprimir a operación",
|
||||
"cmd.ui.assistant": "Mostrar/Ocultar o asistente",
|
||||
"cmd.view.render_browser": "Navegador de render",
|
||||
"cmd.view.render_dock": "Mover o panel de render",
|
||||
"cmd.view.render_focus": "Vista de foco do render",
|
||||
@@ -1288,9 +1303,11 @@
|
||||
"cmd.view_tool": "Ferramenta de navegación",
|
||||
"cmd.view_top": "Vista superior",
|
||||
"cmd.view_visual_style": "Estilo visual",
|
||||
"color.blue": "Azul",
|
||||
"color.copy": "Preme para copiar os valores da cor",
|
||||
"color.edit": "Preme para editar a cor",
|
||||
"color.gamma": "Mostrando os valores da cor no espazo gamma 0-255",
|
||||
"color.green": "Verde",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "Escribe #rrggbb ou #rrggbbaa; con seis díxitos consérvase a opacidade",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1298,6 +1315,7 @@
|
||||
"color.linear": "Mostrando os valores da cor no espazo lineal 0-1",
|
||||
"color.recent": "Cores recentes",
|
||||
"color.recent_help": "As cores que escolles en calquera campo de cor aparecen aquí",
|
||||
"color.red": "Vermello",
|
||||
"color.saturation": "Saturación (porcentaxe)",
|
||||
"color.saturation_value": "Saturación e valor",
|
||||
"color.selected": "Cor seleccionada",
|
||||
@@ -1305,6 +1323,8 @@
|
||||
"color.step_up": "Aumentar un paso",
|
||||
"color.value": "Valor, a luminosidade (porcentaxe)",
|
||||
"config.column_filter_empty": "Non hai columnas que coincidan",
|
||||
"config.rule_parameter_columns": "Só se listan os parámetros que teñen unha columna na táboa. Engade unha con Engadir columna (+) para usala aquí.",
|
||||
"config.rule_suppression_columns": "Só se listan as operacións que teñen unha columna Suprimir na táboa. Engade unha con Engadir columna (+) para controlalas desde aquí.",
|
||||
"contact.components_count": "{count} compoñentes",
|
||||
"contact.enabled_label": "activado",
|
||||
"coupling.gear_1": "Engrenaxe 1",
|
||||
@@ -1353,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "Escolle corpos ou operacións: un bosquexo non é ningunha das dúas cousas. Repite as súas curvas co Patrón ou a Simetría do propio bosquexo, ou extrúeo primeiro.",
|
||||
"dialog.select": "↖ Seleccionar",
|
||||
"dialog.selected_count": "↖ {count} seleccionados",
|
||||
"dialog.selection_constraint": "Restrición {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · curva {id}",
|
||||
"dialog.selection_guidance": "Selecciona unha entrada para cambiala. Fai clic nos obxectos seleccionados para quitalos; Ctrl/Cmd-clic tamén os alterna. Revisa a previsualización e preme Aceptar.",
|
||||
"dialog.selection_midpoint": "Punto medio · {id}",
|
||||
"dialog.selection_profile": "{sketch} · perfil {number}",
|
||||
"dialog.selection_step": "{step} de {total}: selecciona {input}",
|
||||
"dialog.step": "Paso",
|
||||
"document.changed_on_disk": "{name} cambiou no disco",
|
||||
@@ -1436,6 +1460,7 @@
|
||||
"help.construct.axis.two_points": "Crea un eixe de construción por dous puntos, vértices ou puntos do bosquexo. Úsao para un eixe que vai entre dúas posicións coñecidas.",
|
||||
"help.construct.plane.along_path": "Crea un plano de construción perpendicular a unha aresta ou curva, nunha posición ao longo dela. Úsao para debuxar o perfil dun varrido ou unha sección dunha traxectoria.",
|
||||
"help.construct.plane.at_angle": "Crea un plano de construción xirado un ángulo a partir dun plano ou dunha cara, arredor dun eixe ou dunha aresta. Úsao para bosquexos inclinados, como unha escuadra en ángulo.",
|
||||
"help.construct.plane.axis_point": "Crea un plano de construción que contén un eixe e un punto fóra dese eixe. Escolle unha orixe ou un eixe de construción, unha aresta recta ou unha liña do bosquexo e despois un vértice, un punto do bosquexo ou un punto de construción. Un punto sobre o eixe non pode definir un plano único.",
|
||||
"help.construct.plane.midplane": "Crea un plano de construción a medio camiño entre dous planos ou caras planas, ou que os bisecta onde se atopan formando un ángulo. Úsao como plano de simetría.",
|
||||
"help.construct.plane.offset": "Crea un plano de construción paralelo a un plano ou a unha cara plana, a unha distancia ou por un punto. Úsao para debuxar a unha altura fixa sobre unha cara.",
|
||||
"help.construct.plane.perpendicular": "Crea un plano de construción que contén unha liña ou aresta e é perpendicular a un plano ou a unha cara. Úsao para debuxar en vertical sobre un modelo ao longo dunha aresta.",
|
||||
@@ -1812,6 +1837,8 @@
|
||||
"joint_move.blocked_hint": "Comproba o tipo de unión (como Ríxida), a fixación ao chan, os límites da unión e os valores controlados por parámetros.",
|
||||
"joint_move.blocked_title": "Non se pode mover esta peza",
|
||||
"joint_move.coarse": "Paso de rotación preto do eixe (°)",
|
||||
"joint_move.conflict": "Unha unión desta peza está en conflito coas outras: {joint}.",
|
||||
"joint_move.conflict_hint": "Edita, suprime ou elimina a unión en conflito para que as outras unións poidan colocar a peza.",
|
||||
"joint_move.distance": "Distancia desde o inicio",
|
||||
"joint_move.done": "Feito",
|
||||
"joint_move.fine": "Paso de rotación lonxe do eixe (°)",
|
||||
@@ -1911,6 +1938,8 @@
|
||||
"motion.track_name": "Pista {number}",
|
||||
"nav.alias.help": "Shift+Alt+arrastrar co botón esquerdo orbita, Shift+Alt+botón central desprázase, Shift+Alt+botón dereito fai zoom; arrastrar co botón central tamén desprázase e arrastrar co botón dereito orbita",
|
||||
"nav.blender.help": "Arrastrar co botón central orbita, Shift+botón central desprázase, Ctrl+botón central fai zoom; arrastrar co botón dereito non move a cámara",
|
||||
"nav.creo.help": "Arrastrar co botón central orbita, Shift+botón central desprázase, Ctrl+botón central arrastrando en vertical fai zoom (cara abaixo para achegar); xira a roda do rato para facer zoom",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "O rato e o rato 3D xiran a vista a través dos dous polos e conservan o xiro sobre a vista. O axuste dos eixes con Shift está desactivado.",
|
||||
"nav.free_orbit.label": "Órbita libre",
|
||||
"nav.free_orbit.snap_help": "O rato e o rato 3D xiran a vista a través dos dous polos e conservan o xiro sobre a vista. Mantén Shift durante un arrastre de órbita para axustar a dirección da vista e o xiro.",
|
||||
@@ -2063,12 +2092,19 @@
|
||||
"parts.family.button_head_cap_screw": "Parafuso de cabeza abombada con hexágono interior",
|
||||
"parts.family.dowel_pin": "Pasador paralelo (espiga)",
|
||||
"parts.family.hex_bolt": "Parafuso / perno de cabeza hexagonal",
|
||||
"parts.family.hex_cap_screw_inch": "Parafuso de cabeza hexagonal (polgadas)",
|
||||
"parts.family.hex_nut": "Porca hexagonal",
|
||||
"parts.family.hex_nut_inch": "Porca hexagonal (polgadas)",
|
||||
"parts.family.set_screw": "Parafuso sen cabeza de hexágono interior, punta plana",
|
||||
"parts.family.socket_head_cap_screw": "Parafuso de cabeza cilíndrica con hexágono interior",
|
||||
"parts.family.washer": "Arandela plana",
|
||||
"parts.family.washer_sae": "Arandela plana, SAE (estreita)",
|
||||
"parts.family.washer_uss": "Arandela plana, USS (ancha)",
|
||||
"parts.holes": "Furados (opcional)",
|
||||
"parts.placement_hint": "escolle un ou máis furados para asentar unha peza en cada un, ou Aceptar colócaa na orixe",
|
||||
"parts.system": "Sistema de unidades",
|
||||
"parts.system.inch": "Polgadas",
|
||||
"parts.system.metric": "Métrico",
|
||||
"pattern.alignment_2": "Aliñamento 2",
|
||||
"pattern.alignment_hint": "O número inclúe o orixinal. Centrar equilibra as copias; Inverter escolle o lado da copia extra cando o total é par.",
|
||||
"pattern.direction_1": "Dirección 1",
|
||||
@@ -2130,6 +2166,8 @@
|
||||
"prefs.button_left": "Arrastrar co botón esquerdo",
|
||||
"prefs.button_middle": "Arrastrar co botón central",
|
||||
"prefs.button_right": "Arrastrar co botón dereito",
|
||||
"prefs.carousel_next": "Seguinte",
|
||||
"prefs.carousel_previous": "Anterior",
|
||||
"prefs.choose_font": "Escoller ficheiro de tipo de letra…",
|
||||
"prefs.click": "Clic",
|
||||
"prefs.contact_shading": "Sombreado suave de contacto",
|
||||
@@ -2142,6 +2180,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Superficies curvas",
|
||||
"prefs.curves": "Superficies curvas",
|
||||
"prefs.cut_preview_tint": "Mostrar en vermello o material retirado",
|
||||
"prefs.cut_preview_tint_help": "Unha vista previa énchea o material que retira unha operación, e a forma que substitúe, cun fantasma vermello translúcido. Se está desactivado, eses recheos son totalmente transparentes e só se debuxan os seus contornos.",
|
||||
"prefs.dark_parts_shading": "Conservar a forma das pezas escuras",
|
||||
"prefs.dark_parts_shading_help": "Sombrea a vista en luz lineal cunha luz de recheo suave do ceo, para que as caras escuras manteñan un degradado en vez de volverse negras",
|
||||
"prefs.dead_zone_note": "A banda raiada é a zona morta: o movemento dentro dela ignórase.",
|
||||
@@ -2200,6 +2240,8 @@
|
||||
"prefs.interface_language": "Idioma da interface",
|
||||
"prefs.interface_note": "Como se ve SolidCraft e onde están as súas partes.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_curved_roll_arrows": "Inverter as frechas curvas de balanceo",
|
||||
"prefs.invert_curved_roll_arrows_note": "As dúas frechas curvas da esquina do cubo fan xirar a vista no plano da pantalla. Activado: a frecha en sentido horario xira en sentido antihorario e a frecha en sentido antihorario xira en sentido horario. Desactivado: cada frecha xira a vista no sentido en que se curva.",
|
||||
"prefs.invert_navigator_arrows": "Invertir as frechas de rotación do cubo de vistas",
|
||||
"prefs.invert_navigator_arrows_note": "As frechas arredor do cubo fan orbitar a cámara na dirección na que apuntan, como en Fusion e Inventor: a frecha da esquerda apunta á dereita e move a cámara cara á dereita. Desactivado: unha frecha xira o cubo cara ao lado no que está, así que a frecha da esquerda move a cámara cara á esquerda. As frechas curvas de balanceo non se ven afectadas.",
|
||||
"prefs.keep_work_on_tab_switch": "Manter abertos os diálogos e as ferramentas ao cambiar de deseño",
|
||||
@@ -2259,6 +2301,8 @@
|
||||
"prefs.placement": "Colocación",
|
||||
"prefs.prefer_free_sketch_points": "Preferir os puntos de bosquexo móbiles cando os puntos se solapan",
|
||||
"prefs.preview_choices": "Vista previa das túas opcións",
|
||||
"prefs.preview_tint_new_faces": "Colorear a superficie nova nunha vista previa",
|
||||
"prefs.preview_tint_new_faces_help": "A vista previa dunha operación que cambia un corpo pode debuxar a superficie que engade coa cor da vista previa. Se está desactivado, o corpo debúxase tal e como é.",
|
||||
"prefs.print_face_highlight": "Resaltado da cara de impresión",
|
||||
"prefs.print_face_highlight_tip": "Intensidade da tinguidura e do contorno da cara de impresión dun corpo na vista. 0 % non mostra resaltado.",
|
||||
"prefs.profile_active": "Perfil activo",
|
||||
@@ -2556,6 +2600,10 @@
|
||||
"render.base_color": "Cor base · sRGB",
|
||||
"render.base_metallic": "Fracción metálica",
|
||||
"render.base_opaque_only": "Este editor mantén a semántica de corpo opaco. Usa os controis explícitos de vidro para a transmisión óptica.",
|
||||
"render.batch_all_shots": "Todas as tomas ({count})",
|
||||
"render.batch_empty": "Garda unha toma antes de renderizar todas as tomas.",
|
||||
"render.batch_limit": "Renderizar todas as tomas está limitado a 32 tomas.",
|
||||
"render.batch_status": "{running} en curso · {waiting} en espera",
|
||||
"render.bloom_enable": "Mostrar resplandor",
|
||||
"render.bloom_help": "Brillo creativo só de saída a partir de realces lineais positivos, esparexido con tres pasadas de caixa normalizadas. O raio está en píxeles de saída. Engádese na luz de visualización lineal antes de sRGB; non é unha lente, un efecto volumétrico nin unha mellora da converxencia. O EXR en bruto non cambia.",
|
||||
"render.bloom_opaque": "O resplandor require unha saída opaca. O PNG transparente rexéitase; non se aplana sen avisar.",
|
||||
@@ -2666,6 +2714,13 @@
|
||||
"render.choose_look": "Escolle primeiro un estilo en Aparencias",
|
||||
"render.chromatic_offset": "Desprazamento cromático dos bordos",
|
||||
"render.clear_stage": "Quitar o escenario gardado",
|
||||
"render.compare_divider": "Divisor da comparación",
|
||||
"render.compare_live": "En directo",
|
||||
"render.compare_live_preview": "Vista previa en directo",
|
||||
"render.compare_render": "Render {number}",
|
||||
"render.compare_side": "Render {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} s · {renderer} · capturado na revisión {revision}",
|
||||
"render.compare_swap": "Intercambiar",
|
||||
"render.copy_request": "Copiar JSON",
|
||||
"render.cpu_aa_help": "Mostras espaciais: máis mostras melloran os bordos e aumentan o tempo de renderizado.",
|
||||
"render.cpu_area_add": "Engadir luz rectangular da CPU",
|
||||
@@ -3415,6 +3470,10 @@
|
||||
"render.result_retained": "Móstrase a última imaxe completada; un renderizado máis recente non a substituíu.",
|
||||
"render.result_sequence": "Punto de control",
|
||||
"render.result_title": "Resultado do renderizado",
|
||||
"render.results_choose": "Resultados conservados",
|
||||
"render.results_label": "Render {number} · {shot} · {seconds} s",
|
||||
"render.results_stale_label": "{result} [desactualizado]",
|
||||
"render.results_unnamed": "Imaxe",
|
||||
"render.return_design": "Volver a Deseño",
|
||||
"render.review_stale": "Esta revisión está obsoleta. Resólvea ou comproba de novo antes de aplicar.",
|
||||
"render.ribbon_aging": "Envellecemento",
|
||||
@@ -3898,10 +3957,7 @@
|
||||
"the two origins are on the same component": "as dúas orixes están no mesmo compoñente",
|
||||
"thread.automatic_size": "Tamaño automático",
|
||||
"thread.designation": "Designación",
|
||||
"thread.direction": "Dirección da rosca",
|
||||
"thread.iso_metric": "Métrica ISO",
|
||||
"thread.left_hand": "Á esquerda",
|
||||
"thread.right_hand": "Á dereita",
|
||||
"thread.select_size": "Seleccionar o tamaño",
|
||||
"timeline.delete_after_marker_one": "Eliminar 1 operación despois do marcador de historial",
|
||||
"timeline.delete_after_marker_other": "Eliminar {count} operacións despois do marcador de historial",
|
||||
@@ -4075,6 +4131,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Executa isto nun terminal e despois preme Conectar de novo:",
|
||||
"ui.agent_chat_auth_title": "Iniciar sesión no axente",
|
||||
"ui.agent_chat_cancel": "Cancelar",
|
||||
"ui.agent_chat_cancel_timeout": "O axente non respondeu a Deter e desconectouse. Volve conectar para continuar.",
|
||||
"ui.agent_chat_cannot_start": "Instalado, pero non se inicia: {error}",
|
||||
"ui.agent_chat_changes": "Cambios: {n}, un só paso de desfacer",
|
||||
"ui.agent_chat_changes_n": "{n} cambios",
|
||||
@@ -4084,11 +4141,13 @@
|
||||
"ui.agent_chat_command": "Orde do axente",
|
||||
"ui.agent_chat_connect": "Conectar",
|
||||
"ui.agent_chat_copy": "Copiar",
|
||||
"ui.agent_chat_copy_message": "Copiar a mensaxe",
|
||||
"ui.agent_chat_ctx_bodies": "Corpos",
|
||||
"ui.agent_chat_ctx_elements": "Elementos",
|
||||
"ui.agent_chat_ctx_features": "Operacións",
|
||||
"ui.agent_chat_ctx_more": "+{n} máis",
|
||||
"ui.agent_chat_ctx_other": "Outros",
|
||||
"ui.agent_chat_custom_heading": "Modelo personalizado",
|
||||
"ui.agent_chat_custom_hint": "Calquera programa que fale o Agent Client Protocol por stdin e stdout. Non se executa en ningún shell.",
|
||||
"ui.agent_chat_denied": "Denegado",
|
||||
"ui.agent_chat_deny": "Denegar",
|
||||
@@ -4132,8 +4191,16 @@
|
||||
"ui.agent_chat_mode_all": "Todo",
|
||||
"ui.agent_chat_mode_ask": "Preguntar",
|
||||
"ui.agent_chat_mode_edits": "Edicións",
|
||||
"ui.agent_chat_model_custom": "Usar o personalizado: {model}",
|
||||
"ui.agent_chat_model_no_tools": "{model} non pode usar as ferramentas de SolidCraft, así que só pode conversar. Escolle un modelo que admita ferramentas.",
|
||||
"ui.agent_chat_model_rejected": "O axente non aceptou {model}: {error}",
|
||||
"ui.agent_chat_model_search": "Buscar modelos…",
|
||||
"ui.agent_chat_model_tip": "O modelo que usa o axente. A túa escolla lémbrase para este axente.",
|
||||
"ui.agent_chat_models_failed": "Non se puideron listar os modelos: {error}",
|
||||
"ui.agent_chat_models_heading": "Modelos dispoñibles",
|
||||
"ui.agent_chat_models_listing": "Listando os modelos…",
|
||||
"ui.agent_chat_models_none": "O punto de acceso non lista ningún modelo",
|
||||
"ui.agent_chat_models_not_a_list": "A resposta non era unha lista de modelos",
|
||||
"ui.agent_chat_new": "Chat novo",
|
||||
"ui.agent_chat_new_confirm": "Comezar un chat novo? Borrarase o actual.",
|
||||
"ui.agent_chat_next_picture": "Imaxe seguinte",
|
||||
@@ -4407,14 +4474,21 @@
|
||||
"ui.construction.contact_hint": "Tanxente: escolle un círculo/arco ou unha spline/cónica aberta e despois o punto de tanxencia (curvas de forma libre: extremos)",
|
||||
"ui.construction.external_hint": "Tanxente: escolle un círculo ou arco, un punto exterior e despois a solución desexada",
|
||||
"ui.construction.free_hint": "Recta de construción: fai clic en dous puntos; clic dereito para reiniciar",
|
||||
"ui.construction.normal_needs_length": "Unha recta infinita é sempre de construción: escribe unha lonxitude para debuxar unha liña normal",
|
||||
"ui.construction.point_hint": "Recta de construción: fai clic no punto polo que debe pasar",
|
||||
"ui.construction.reference_hint": "Recta de construción: escolle unha recta e despois un punto; clic dereito para cambiar a referencia",
|
||||
"ui.construction.ribbon_label": "Liñas aux.",
|
||||
"ui.construction.side_hint": "Recta de construción: fai clic no lado cara ao que segue a recta; clic dereito para comezar de novo",
|
||||
"ui.context_menu": "Menú contextual",
|
||||
"ui.continuity_crease": "Aresta",
|
||||
"ui.continuity_curvature": "Curvatura",
|
||||
"ui.continuity_curvature_change": "Cambio de curvatura",
|
||||
"ui.continuity_g0": "G0 aresta",
|
||||
"ui.continuity_g1": "G1 tanxente",
|
||||
"ui.continuity_g2": "G2 curvatura",
|
||||
"ui.continuity_gap": "Separación",
|
||||
"ui.continuity_position": "Posición",
|
||||
"ui.continuity_tangent": "Tanxente",
|
||||
"ui.copy": "Copiar",
|
||||
"ui.corner": "Esquina",
|
||||
"ui.corners": "Esquinas",
|
||||
@@ -4675,6 +4749,7 @@
|
||||
"ui.hideOrigin": "Ocultar a orixe",
|
||||
"ui.hidePlanes": "Ocultar os planos",
|
||||
"ui.hideProfile": "Ocultar o perfil",
|
||||
"ui.hide_component_history": "Ocultar o seu historial",
|
||||
"ui.hide_origin_by_default": "Ocultar a orixe de forma predeterminada",
|
||||
"ui.hide_origin_note": "Os deseños novos comezan co cartafol Orixe oculto. Un deseño conserva o seu propio axuste de Orixe cando o mostras ou o ocultas.",
|
||||
"ui.history_back": "Retroceder un paso",
|
||||
@@ -4682,6 +4757,7 @@
|
||||
"ui.history_edge_hint": "fai dobre clic na aresta",
|
||||
"ui.history_first": "Ir ao comezo",
|
||||
"ui.history_forward": "Avanzar un paso",
|
||||
"ui.history_hide_hidden": "Ocultar os compoñentes ocultos",
|
||||
"ui.history_hide_names": "Ocultar os nomes",
|
||||
"ui.history_last": "Ir ao final",
|
||||
"ui.history_marker_tip": "Marcador de historial: arrastra para retroceder o modelo; as operacións novas van aquí",
|
||||
@@ -4691,10 +4767,12 @@
|
||||
"ui.history_play_button_show": "Mostrar o botón Reproducir",
|
||||
"ui.history_play_finish_action": "Remata a acción actual antes de reproducir o historial",
|
||||
"ui.history_play_roll_back": "Move o marcador do historial cara atrás antes de reproducir",
|
||||
"ui.history_show_hidden": "Mostrar os compoñentes ocultos",
|
||||
"ui.history_show_names": "Mostrar os nomes",
|
||||
"ui.history_sketch_listed_under": "Listado en {feature}",
|
||||
"ui.history_title": "Historial",
|
||||
"ui.history_toggle_names": "Mostrar ou ocultar os nomes das operacións",
|
||||
"ui.history_waits_for_editor": "Desfacer, Refacer e Historial agardan ata que se abra o editor da operación",
|
||||
"ui.hole": "Furado",
|
||||
"ui.hole_tap_type": "Tipo de roscado do furado",
|
||||
"ui.hole_type": "Tipo de furado",
|
||||
@@ -4710,6 +4788,7 @@
|
||||
"ui.insert_design.saved_file": "{name} ten cambios sen gardar, así que se inseriu o seu ficheiro gardado. Gárdao e despois usa Actualizar as ligazóns para incorporar os cambios.",
|
||||
"ui.insert_design.unsaved": "Cambios sen gardar",
|
||||
"ui.inspect": "Inspeccionar",
|
||||
"ui.instances": "Instancias",
|
||||
"ui.intersect": "Intersecar",
|
||||
"ui.isolate": "Illar",
|
||||
"ui.join": "Unir",
|
||||
@@ -4974,6 +5053,7 @@
|
||||
"ui.showDimension": "Mostrar a cota",
|
||||
"ui.showOrigin": "Mostrar a orixe",
|
||||
"ui.showPlanes": "Mostrar os planos",
|
||||
"ui.show_component_history": "Mostrar o seu historial",
|
||||
"ui.show_constraints": "Restricións",
|
||||
"ui.show_construction_geometries": "Xeometrías de construción",
|
||||
"ui.show_dimensions": "Cotas",
|
||||
@@ -5156,7 +5236,7 @@
|
||||
"update.check_unavailable": "A busca de actualizacións non está dispoñible para esta instalación.",
|
||||
"update.checking": "Comprobando…",
|
||||
"update.copy": "Copiar",
|
||||
"update.dmg_opened": "A imaxe de disco está aberta e verificada: arrastra SolidCraft a Aplicacións, substituíndo a copia antiga, e despois reinicia SolidCraft.",
|
||||
"update.dmg_opened": "A imaxe de disco está aberta e verificada: sae de SolidCraft, arrastra o novo SolidCraft a Aplicacións, substituíndo a copia antiga, e despois volve abrir SolidCraft.",
|
||||
"update.download": "Descargar",
|
||||
"update.download_open": "Descargar e abrir",
|
||||
"update.downloading": "Descargando e verificando… {done} de {total} MB",
|
||||
@@ -5200,6 +5280,7 @@
|
||||
"web.open_link": "Abrir o deseño ligado?",
|
||||
"web.open_link_confirm": "Hai cambios sen gardar. Abrir o deseño ligado nunha pestana nova?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ escoller · Intro / Tab completar · Esc pechar",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ escoller · Enter aplica este valor",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ escoller · Enter aplica o que escribiches · Esc pechar",
|
||||
"⌘Enter continues on GitHub.": "⌘Enter continúa en GitHub."
|
||||
}
|
||||
|
||||
+188
-14
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "מלבני",
|
||||
"Rectangular Tube": "צינור מלבני",
|
||||
"Reference face": "פאת ייחוס",
|
||||
"Reference plane": "מישור ייחוס",
|
||||
"Relief Depth": "עומק הקלה",
|
||||
"Relief Width": "רוחב הקלה",
|
||||
"Remove Tools": "הסרת הכלים",
|
||||
@@ -494,7 +495,8 @@
|
||||
"Rename Folder": "שינוי שם תיקייה",
|
||||
"Render Stage": "שלב הרינדור",
|
||||
"Reopen designs": "פתיחה מחדש של תכנים",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "פתיחה מחדש של התכנים שהיו פתוחים כש־SolidCraft נסגרה בפעם האחרונה",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "פתיחה מחדש של התכנונים שהיו פתוחים בפעם הקודמת",
|
||||
"Repair Part": "תיקון חלק",
|
||||
"Replace existing library file": "החלפת קובץ הספרייה הקיים",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "דיווח על באג — בדיקת צילום מסך ופתיחת טיוטה ב־GitHub",
|
||||
"Report copied. Keep this text to submit on GitHub when you are online.": "הדיווח הועתק. יש לשמור את הטקסט כדי לשלוח אותו ב-GitHub כשהחיבור לאינטרנט יהיה זמין.",
|
||||
@@ -789,6 +791,7 @@
|
||||
"browser.edit_canvas_title": "עריכת קנבס: {name}",
|
||||
"browser.edit_point_cloud_title": "עריכת ענן נקודות: {name}",
|
||||
"browser.move_title": "הזזה: {name}",
|
||||
"browser.needs_repair": "{name}: דרוש תיקון",
|
||||
"browser.parameters": "פרמטרים ({count})",
|
||||
"browser.precision": "דיוק: {precision}",
|
||||
"browser.sketch_attachment_lost": "{name} (ההצמדה אבדה)",
|
||||
@@ -833,6 +836,7 @@
|
||||
"cmd.assembly_mass_properties": "תכונות מסה",
|
||||
"cmd.body.print_face": "פאת הדפסה",
|
||||
"cmd.body_rename": "שינוי שם גוף",
|
||||
"cmd.body_repair": "תיקון חלק",
|
||||
"cmd.browser_collapse_all": "כיווץ הכול",
|
||||
"cmd.browser_expand_all": "הרחבת הכול",
|
||||
"cmd.browser_group": "קבוצה חדשה",
|
||||
@@ -917,6 +921,8 @@
|
||||
"cmd.diagnostics_crash_reports": "דוחות קריסה",
|
||||
"cmd.diagnostics_crash_reports_clear": "מחיקת דוחות קריסה",
|
||||
"cmd.diagnostics_crash_test": "בדיקת קריסה",
|
||||
"cmd.diagnostics_profile": "פרופיל ביצועים",
|
||||
"cmd.diagnostics_validation": "אימות",
|
||||
"cmd.doc_recover": "שחזור תכן",
|
||||
"cmd.doc_recovery_discard": "השלכת תכן משוחזר",
|
||||
"cmd.doc_recovery_list": "תכנים לשחזור",
|
||||
@@ -954,6 +960,7 @@
|
||||
"cmd.frame_profile_list": "ספריית פרופילים",
|
||||
"cmd.frame_profile_properties": "מאפייני פרופיל",
|
||||
"cmd.inspect_accessibility": "ניתוח נגישות",
|
||||
"cmd.inspect_analysis": "ניתוח תצוגה",
|
||||
"cmd.inspect_center_of_mass": "מרכז מסה",
|
||||
"cmd.inspect_clearance": "מרווח",
|
||||
"cmd.inspect_colored_components": "הפעלת צבעי רכיבים",
|
||||
@@ -977,6 +984,7 @@
|
||||
"cmd.joint_delete": "מחיקת מפרק",
|
||||
"cmd.joint_drive": "הנעת מפרקים",
|
||||
"cmd.joint_edit": "עריכת מפרק",
|
||||
"cmd.joint_find_limits": "מציאת גבולות מפרק",
|
||||
"cmd.joint_gear": "גלגל שיניים",
|
||||
"cmd.joint_inspect": "בדיקת מפרק",
|
||||
"cmd.joint_limits": "גבולות מפרק",
|
||||
@@ -1358,6 +1366,7 @@
|
||||
"crash.unclean_explanation": "זוהה בהפעלה מאוחרת יותר; זמן היציאה המדויק לא נרשם. גם כיבוי של המערכת או עצירה בכפייה עלולים לגרום ליציאה לא תקינה.",
|
||||
"delete.and_more": "… ועוד {count}",
|
||||
"diagnostic.empty_boolean_component": "אין גוף להסרה או לחיתוך משותף ברכיב '{component}'; ייתכן שהכלי חופף ל־'{body}' ברכיב '{other}' — יש להפעיל את הרכיב הזה או לבחור את הגוף כיעד",
|
||||
"diagnostic.reselect_reference": "{feature}: ההפניה `{reference}` כבר אינה קיימת כפי שנבחרה; יש לבחור אותה מחדש",
|
||||
"dialog.bodies_count": "{count} גופים",
|
||||
"dialog.clearance_invalid": "המרווח חייב להיות אורך של 0 ומעלה",
|
||||
"dialog.compute": "חישוב",
|
||||
@@ -1423,6 +1432,14 @@
|
||||
"file_load.reading_percent": "קורא את הקובץ... {percent}%",
|
||||
"file_load.rebuilding": "בונה מחדש פיצ'ר {done} מתוך {total}",
|
||||
"file_load.recovering": "משחזר את התכן",
|
||||
"file_load.step_build": "בונה את המודל",
|
||||
"file_load.step_feature": "תכונה {done} מתוך {total}",
|
||||
"file_load.step_part": "חלק {done} מתוך {total}",
|
||||
"file_load.step_percent": "{percent}%",
|
||||
"file_load.step_read": "קורא את הקובץ",
|
||||
"file_load.step_rebuild": "בונה מחדש את התכונות",
|
||||
"file_load.step_repair": "בודק ומתקן את החלקים",
|
||||
"file_load.step_show": "מכין את התצוגה",
|
||||
"file_load.stopping": "ממתין לעצירת הפעולה הקודמת...",
|
||||
"file_load.updating_links": "עדכון קישורים",
|
||||
"file_load.waiting": "ממתין לפעולת קובץ",
|
||||
@@ -1717,6 +1734,7 @@
|
||||
"help.timeline.delete": "מחיקת הפיצ'רים שנבחרו מציר הזמן; מחיקת סקיצה מוחקת גם את הפיצ'רים שנבנו ממנה. מתאים להסרת שלב שכבר אין בו צורך.",
|
||||
"help.timeline.dependents": "הצגת הפיצ'רים שיימחקו יחד עם פיצ'ר, ואלה שייכשלו בלעדיו. מתאים לפני מחיקה או שינוי של פיצ'ר.",
|
||||
"help.timeline.play": "הפעלה מחדש של הפיצ'רים שנותרו, אחד בכל פעם, עם השהיה של חצי שנייה לאחר כל בנייה מחדש. להפסקה יש להפעיל שוב את הפקודה או ללחוץ על Escape; מעבר לסוף מתבצע מיד.",
|
||||
"help.version": "גרסה {version}",
|
||||
"help.view.apply": "סיבוב התצוגה לכיוון סטנדרטי כמו חזית, מבט עליון או איזומטרי, או לתצוגה שמורה בשם. מתאים להתבוננות במודל מזווית קבועה.",
|
||||
"help.view.delete": "מחיקת תצוגה שמורה. המודל אינו משתנה.",
|
||||
"help.view.fit": "זום והזזה כך שכל המודל, או הבחירה, ימלאו את התצוגה. מתאים כשהמודל יצא מהטווח הנראה.",
|
||||
@@ -1804,6 +1822,27 @@
|
||||
"home.start_hint": "אפשר ליצור תכן, לפתוח קובץ אחרון או לעיין בדוגמה.",
|
||||
"home.whats_new": "מה חדש",
|
||||
"home.yesterday": "אתמול",
|
||||
"import_report.details": "פרטים:",
|
||||
"import_report.file_one": "הייתה בעיה נוספת אחת בקובץ.",
|
||||
"import_report.file_other": "היו {count} בעיות נוספות בקובץ.",
|
||||
"import_report.hint": "ייצוא מחדש של החלקים האלה מהתוכנה שיצרה אותם עשוי לפתור זאת. ב„העתקת הודעת האזהרה” יש את הפרטים.",
|
||||
"import_report.left_out_one": "לא ניתן היה לתקן פאה אחת והיא חסרה.",
|
||||
"import_report.left_out_other": "לא ניתן היה לתקן {count} פאות והן חסרות.",
|
||||
"import_report.needs_repair_one": "חלק אחד דורש תיקון לפני שאפשר למדל עליו.",
|
||||
"import_report.needs_repair_other": "{count} חלקים דורשים תיקון לפני שאפשר למדל עליהם.",
|
||||
"import_report.not_imported_one": "לא ניתן היה לייבא חלק אחד.",
|
||||
"import_report.not_imported_other": "לא ניתן היה לייבא {count} חלקים.",
|
||||
"import_report.not_shown_one": "לא ניתן היה לצייר פאה אחת והיא מוסתרת.",
|
||||
"import_report.not_shown_other": "לא ניתן היה לצייר {count} פאות והן מוסתרות.",
|
||||
"import_report.open_one": "בחלק אחד יש רווחים בין הפאות והוא יובא כגוף פתוח.",
|
||||
"import_report.open_other": "ב-{count} חלקים יש רווחים בין הפאות והם יובאו כגופים פתוחים.",
|
||||
"import_report.refused_one": "לא ניתן היה להפוך חלק אחד לגוף מוצק והוא לא יובא.",
|
||||
"import_report.refused_other": "לא ניתן היה להפוך {count} חלקים לגופים מוצקים והם לא יובאו.",
|
||||
"import_report.repaired_one": "תוקנו פגמים קטנים בחלק אחד.",
|
||||
"import_report.repaired_other": "תוקנו פגמים קטנים ב-{count} חלקים.",
|
||||
"import_report.solids": "{solids} מתוך {parts} חלקים יובאו כגופים מוצקים.",
|
||||
"import_report.surfaces_one": "לא ניתן היה להפוך חלק אחד לגוף מוצק והוא יובא כמשטחים.",
|
||||
"import_report.surfaces_other": "לא ניתן היה להפוך {count} חלקים לגופים מוצקים והם יובאו כמשטחים.",
|
||||
"install.button_tip": "התקנת SolidCraft כיישום: נפתח בחלון משלו ועובד גם ללא חיבור לרשת",
|
||||
"install.dock_1": "יש לבחור קובץ › הוספה ל־Dock.",
|
||||
"install.dock_2": "יש ללחוץ על הוספה.",
|
||||
@@ -1828,7 +1867,18 @@
|
||||
"issue.note_save_failed": "לא ניתן לשמור את צילום המסך: {error}",
|
||||
"issue.note_saved": "צילום המסך נשמר. יש לצרף את קובץ ה־PNG בטיוטה ב־GitHub.",
|
||||
"issue.save_screenshot": "שמירת צילום המסך…",
|
||||
"joint.find_limits": "מציאת גבולות",
|
||||
"joint.find_limits_failed": "החיפוש אחר הגבולות נעצר עקב שגיאה",
|
||||
"joint.find_limits_hint": "ממלא את הגבולות לפי המקום שבו החלקים שהמפרק מזיז נוגעים לראשונה בחלק אחר, בסיבוב או בהחלקה לשני הכיוונים מהמקום הנוכחי. החיפוש רץ על עותק: דבר אינו משתנה עד ללחיצה על OK.",
|
||||
"joint.find_limits_stale": "המפרק או התכן השתנו מאז שהגבולות האלה נמצאו: חפשו אותם שוב",
|
||||
"joint.finding_limits": "מחפש גבולות…",
|
||||
"joint.from_bodies_hint": "כל גוף שנבחר עובר לרכיב חדש משלו ונשאר במקומו.",
|
||||
"joint.limit_contact": "{side} {value}: {a} נוגע ב-{b}",
|
||||
"joint.limit_limit": "{side} {value}: הגבול של ערך אחר או של מפרק אחר",
|
||||
"joint.limit_loop": "{side} {value}: לולאה סגורה אינה יכולה להיסגר מעבר לכך",
|
||||
"joint.limit_range": "{side}: אין מגע בטווח {range}, נשאר כפי שהיה",
|
||||
"joint.limit_turn": "{side} {value}: אין מגע בסיבוב אחד",
|
||||
"joint.limits_overlapping": "כבר חודרים זה לזה כאן: {pairs}. הם עוצרים את החיפוש רק אם יחדרו עמוק יותר.",
|
||||
"joint.one_body": "שתי הבחירות נמצאות על {body}. יש לבחור את השנייה על חלק אחר.",
|
||||
"joint.one_component": "חלקים אלה הם גופים של רכיב אחד: {bodies}. מפרק מזיז רכיב אחד ביחס לרכיב אחר, ולכן כל חלק צריך רכיב משלו.",
|
||||
"joint.rigid_group_one_component": "כל {count} הגופים האלה נמצאים ברכיב אחד (או בתכן עצמו): יש להשתמש קודם ב־{command}.",
|
||||
@@ -2091,12 +2141,19 @@
|
||||
"parts.family.button_head_cap_screw": "בורג ראש כפתור עם שקע משושה",
|
||||
"parts.family.dowel_pin": "פין גלילי",
|
||||
"parts.family.hex_bolt": "בורג ראש משושה",
|
||||
"parts.family.hex_cap_screw_inch": "בורג ראש משושה (אינץ')",
|
||||
"parts.family.hex_nut": "אום משושה",
|
||||
"parts.family.hex_nut_inch": "אום משושה (אינץ')",
|
||||
"parts.family.set_screw": "בורג אלן ללא ראש, קצה שטוח",
|
||||
"parts.family.socket_head_cap_screw": "בורג ראש גלילי עם משושה פנימי",
|
||||
"parts.family.washer": "דסקית שטוחה",
|
||||
"parts.family.washer_sae": "דסקית שטוחה, SAE (צרה)",
|
||||
"parts.family.washer_uss": "דסקית שטוחה, USS (רחבה)",
|
||||
"parts.holes": "חורים (אופציונלי)",
|
||||
"parts.placement_hint": "בחרו חור אחד או יותר כדי להושיב חלק בכל אחד, או לחצו אישור כדי למקם אותו בראשית",
|
||||
"parts.system": "מערכת יחידות",
|
||||
"parts.system.inch": "אינץ'",
|
||||
"parts.system.metric": "מטרי",
|
||||
"pattern.alignment_2": "יישור 2",
|
||||
"pattern.alignment_hint": "הספירה כוללת את המקור. מרכוז מאזן את העותקים; היפוך בוחר את הצד שמקבל את העותק הנוסף כשהסך זוגי.",
|
||||
"pattern.direction_1": "כיוון 1",
|
||||
@@ -2135,6 +2192,18 @@
|
||||
"prefs.alt_frees_manipulators": "Alt / Option משחרר גם את חיצי המניפולטור",
|
||||
"prefs.anti_aliasing_amd_dx12": "החלקת הקצוות משתמשת ב־1× כי בדיקת ההפעלה לא ציירה דבר עם דגימה מרובה ועברה ב־1×. אפשר לבחור הגדרה אחרת למעלה; יש להפעיל מחדש כדי להחיל אותה אם הבדיקה עוברת.",
|
||||
"prefs.anti_aliasing_note": "מחליק קצוות משוננים.",
|
||||
"prefs.appearance.colour_vision_help": "משנה צבעים בעלי משמעות: מצבי סקיצה, צירים, כיפופים, ניתוחים וסמלים.",
|
||||
"prefs.appearance.desktop_only": "למחשב בלבד",
|
||||
"prefs.appearance.follow_help": "טוען מחדש כאשר כלי אחר משכתב את הקובץ.",
|
||||
"prefs.appearance.interface_scale": "קנה מידה של הממשק",
|
||||
"prefs.appearance.next_themes": "ערכות העיצוב הבאות",
|
||||
"prefs.appearance.option_count": "{count} אפשרויות",
|
||||
"prefs.appearance.option_count_one": "{count} אפשרות",
|
||||
"prefs.appearance.previous_themes": "ערכות העיצוב הקודמות",
|
||||
"prefs.appearance.theme_count": "{count} ערכות עיצוב",
|
||||
"prefs.appearance.theme_own": "צבעי ערכת העיצוב",
|
||||
"prefs.appearance.viewport_colours": "צבעי תצוגת תלת־ממד",
|
||||
"prefs.appearance.viewport_logo": "חלון התצוגה והלוגו",
|
||||
"prefs.applies_on_restart": "ייכנס לתוקף בהפעלה מחדש",
|
||||
"prefs.assistant": "עוזר",
|
||||
"prefs.auto_operation": "בחירת הפעולה באופן אוטומטי",
|
||||
@@ -2149,6 +2218,8 @@
|
||||
"prefs.bottom_centre": "למטה במרכז",
|
||||
"prefs.bottom_right": "למטה מימין",
|
||||
"prefs.browser": "עץ התכן",
|
||||
"prefs.browser_components_open": "רכיבים פתוחים מההתחלה",
|
||||
"prefs.browser_components_open_note": "כל רכיב בעץ התכן מתחיל פתוח, כמו קודם, במקום סגור עד שתפתח אותו.",
|
||||
"prefs.browser_eye_left": "סמל העין ליד חץ הקיפול",
|
||||
"prefs.browser_eye_left_note": "שורות עץ התכן מציגות את סמל העין ליד חץ הקיפול, כמו קודם, במקום בעמודה בקצה הימני של השורות.",
|
||||
"prefs.browser_reveal_on_hover": "הצגה בריחוף",
|
||||
@@ -2198,7 +2269,19 @@
|
||||
"prefs.font_sample": "טקסט לדוגמה בגופן · Ø12.50 mm · 45.0° · 0123456789",
|
||||
"prefs.font_unreadable": "לא ניתן היה לקרוא את הגופן שנבחר.",
|
||||
"prefs.general": "כללי",
|
||||
"prefs.general_note": "שפה, שמירה ועדכונים.",
|
||||
"prefs.general.active": "פעיל",
|
||||
"prefs.general.crash_reports": "דוחות קריסה",
|
||||
"prefs.general.edited": "נערך",
|
||||
"prefs.general.held": "נכלל",
|
||||
"prefs.general.import": "ייבוא...",
|
||||
"prefs.general.last_check": "בדיקה אחרונה: {time}",
|
||||
"prefs.general.minutes": "דקות",
|
||||
"prefs.general.name_setup": "תנו שם להגדרה זו",
|
||||
"prefs.general.profile_name": "שם הפרופיל",
|
||||
"prefs.general.save_new": "שמירה כחדש",
|
||||
"prefs.general.step_import": "STEP וייבוא",
|
||||
"prefs.general.this_computer": "מחשב זה",
|
||||
"prefs.general_note": "פרופילים, שפה, קבצים ועדכונים.",
|
||||
"prefs.gesture_orbit": "סיבוב מבט",
|
||||
"prefs.gesture_pan": "הזזת מבט",
|
||||
"prefs.gesture_zoom": "זום",
|
||||
@@ -2255,10 +2338,10 @@
|
||||
"prefs.logo_accent": "צבע הדגשה",
|
||||
"prefs.logo_theme": "צבעי ערכת הנושא",
|
||||
"prefs.max_file_size": "גודל קובץ מרבי לפתיחה",
|
||||
"prefs.max_file_size_design": "קובצי תכן",
|
||||
"prefs.max_file_size_design": "הקובץ הגדול ביותר לפתיחה: תכנון",
|
||||
"prefs.max_file_size_flag": "ההפעלה הנוכחית החלה עם --max-file-size {size}, שחל עד סגירת SolidCraft. הגדרות אלה חלות בהפעלה ללא אפשרות זו.",
|
||||
"prefs.max_file_size_note": "ברירות מחדל: 512 MB ו־256 MB; לכל היותר 16 GB. פתיחת קובץ דורשת זיכרון בנפח גדול פי כמה מגודלו. תכן ששומר ייבוא גדול עשוי לדרוש מגבלה גבוהה יותר לקובצי תכן כדי לשמור ולפתוח אותו מחדש.",
|
||||
"prefs.max_file_size_step": "קובצי STEP וייבוא",
|
||||
"prefs.max_file_size_step": "הקובץ הגדול ביותר לפתיחה: STEP וייבוא",
|
||||
"prefs.max_file_size_web": "יישום הדפדפן פותח קובצי STEP וייבוא עד 512 MB וקובצי תכן עד 256 MB: לשונית דפדפן אינה יכולה להכיל קבצים גדולים יותר. ביישום למחשב ניתן להעלות מגבלות אלה.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "סמלים גדולים",
|
||||
@@ -2275,7 +2358,7 @@
|
||||
"prefs.navigator_fit_note": "לחיצה על פאה, פינה או חץ בקובייה, או על ציר בגיזמו, גם מביטה אל מרכז המודל מהמרחק האחד שמכיל אותו מכל צד (הכדור החוסם שלו). תצוגת בית משתמשת באותו זום ובאותו מרכז כל עוד זה פעיל, כך שתצוגות שנבחרות אחריה רק מסתובבות. כבוי: התצוגה מסתובבת במקומה ושומרת על היעד ועל הזום.",
|
||||
"prefs.network_host_remove": "הסרה",
|
||||
"prefs.network_locations": "התרת מיקומי רשת בתכנים",
|
||||
"prefs.network_locations_note": "כבוי: קבצים מקושרים של תכן במחשב אחר (\\\\server\\share) נפתחים רק מהשרתים הרשומים כאן, או אחרי שמתירים שרת כשנשאלים.",
|
||||
"prefs.network_locations_note": "לארגונים השומרים תכנונים בשרתי קבצים. כשהאפשרות כבויה: רק שרתים שאישרתם.",
|
||||
"prefs.network_locations_section": "מיקומי רשת",
|
||||
"prefs.not_on_gpu": "לא זמין במעבד הגרפי הזה",
|
||||
"prefs.on": "פועל",
|
||||
@@ -2301,10 +2384,12 @@
|
||||
"prefs.profile_applied": "הפרופיל הוחל.",
|
||||
"prefs.profile_changed": "ההגדרות השתנו מאז שהפרופיל נשמר. „שמירה“ מכניסה אותן לפרופיל.",
|
||||
"prefs.profile_deleted": "הפרופיל נמחק.",
|
||||
"prefs.profile_export": "ייצוא…",
|
||||
"prefs.profile_export": "ייצוא",
|
||||
"prefs.profile_exported": "הפרופיל יוצא.",
|
||||
"prefs.profile_imported": "הפרופיל יובא. בחרו בו תחת „פרופיל פעיל“ כדי לעבור אליו.",
|
||||
"prefs.profile_imported": "הפרופיל יובא.",
|
||||
"prefs.profile_name_hint": "שם הפרופיל, למשל מחשב נייד",
|
||||
"prefs.profile_operations": "מדידת זמני פעולות",
|
||||
"prefs.profile_operations_note": "מתעד כמה זמן לוקחות קריאות SMLib, פעולות ליבה, תכונות, פקודות וייבוא. סוכנים קוראים זאת עם diagnostics.profile.",
|
||||
"prefs.profile_rename": "שינוי שם",
|
||||
"prefs.profile_renamed": "שם הפרופיל שונה.",
|
||||
"prefs.profile_save": "שמירה",
|
||||
@@ -2323,16 +2408,20 @@
|
||||
"prefs.render_previous_geometry_budget": "שימוש בתקציב הגאומטריה הקודם לתצוגה מקדימה של רינדור",
|
||||
"prefs.render_previous_geometry_budget_help": "שחזור מגבלת הגאומטריה הקבועה הקודמת (512 MiB במחשב שולחני של 64 סיביות, 128 MiB בפלטפורמות אחרות) במקום התקציב המבוסס על ההתקן.",
|
||||
"prefs.reports_saved": "{n} שמורים",
|
||||
"prefs.restore_page_defaults": "שחזור ברירות המחדל של עמוד זה",
|
||||
"prefs.ribbon_click_ends_tool": "לחיצה על הלחצן של הכלי הפעיל מסיימת אותו",
|
||||
"prefs.ribbon_click_ends_tool_note": "לחיצה על לחצן הסרט המודגש של כלי השרטוט שבשימוש מסיימת את הכלי, כך שלחיצות בתצוגה בוחרות שוב. כבוי: הלחיצה מפעילה את הכלי מחדש.",
|
||||
"prefs.ribbon_reveal": "הצגת תוצאות חיפוש בסרט",
|
||||
"prefs.ribbon_reveal_note": "מעבר עם הסמן מעל תוצאה בחיפוש פקודות מראה היכן הפקודה נמצאת בסרט: הלשונית, הכפתור או השורה שלה בתפריט הנפתח של לוח. פקודה שהופעלה מהחיפוש מהבהבת שם, ופקודה פעילה מתפריט נפתח מסמנת את הלוח שלה.",
|
||||
"prefs.right_click": "לחיצה ימנית",
|
||||
"prefs.saving": "שמירה אוטומטית ושחזור",
|
||||
"prefs.saving": "הפעלה ושמירה",
|
||||
"prefs.scale_note": "גם Ctrl + / Ctrl − בחלון. הכפתורים בגודל 28 px ב־100%.",
|
||||
"prefs.scale_text": "קנה מידה וטקסט",
|
||||
"prefs.search": "חיפוש הגדרות",
|
||||
"prefs.search_empty": "אין הגדרות תואמות",
|
||||
"prefs.section_hatch": "קווקוו חתך",
|
||||
"prefs.section_hatch_by_part": "לפי חלק",
|
||||
"prefs.section_hatch_one": "סגנון אחד",
|
||||
"prefs.select": "בחירה",
|
||||
"prefs.select_one_item": "בחירת פריט אחד",
|
||||
"prefs.select_other_hidden": "בחירת גאומטריה מוסתרת באמצעות בחירה אחרת",
|
||||
@@ -2447,6 +2536,12 @@
|
||||
"prefs.under_cursor": "מתחת לסמן",
|
||||
"prefs.units_precision": "יחידות ודיוק",
|
||||
"prefs.updates": "עדכונים",
|
||||
"prefs.validation": "בדיקת גופים",
|
||||
"prefs.validation.each_feature": "פעם אחת לכל תכונה",
|
||||
"prefs.validation.each_step": "אחרי כל שלב",
|
||||
"prefs.validation.label": "הבדיקה המלאה של הקרנל",
|
||||
"prefs.validation.off": "כבוי (ניסיוני)",
|
||||
"prefs.validation_note": "אחרי כל שלב: בודק כל סחיפה, פעולה בוליאנית ועיגול.\nפעם אחת לכל תכונה: בודק את התוצאה של כל תכונה פעם אחת.\nכבוי (ניסיוני): אין בדיקה.",
|
||||
"prefs.value_boxes_in_view": "הצגת ערכים ליד הידיות שלהם בתצוגה",
|
||||
"prefs.value_boxes_in_view_tip": "ערך הפקודה (מרחק, רדיוס…) בתיבה ליד הידית שלו, מעל המודל. כבוי: מקלידים אותו בחלון הפקודה, שמקבל את המקלדת אחרי בחירה, ושום דבר לא מסתיר את המודל.",
|
||||
"prefs.view": "התנהגות המבט",
|
||||
@@ -2481,6 +2576,17 @@
|
||||
"recovery.just_now": "זה עתה",
|
||||
"recovery.min_ago": "לפני {count} דק'",
|
||||
"recovery.never_saved": "לא נשמר מעולם",
|
||||
"refused.change": "לא ניתן היה לשנות את {body}.",
|
||||
"refused.cut": "לא ניתן היה לחתוך את {body}.",
|
||||
"refused.face_curved": "אחת הפאות המעוקלות שלו אינה ניתנת לשימוש במידול.",
|
||||
"refused.face_flat": "אחת הפאות השטוחות שלו אינה ניתנת לשימוש במידול.",
|
||||
"refused.face_freeform": "אחת הפאות החופשיות שלו אינה ניתנת לשימוש במידול.",
|
||||
"refused.face_rounded": "אחת הפאות המעוגלות שלו אינה ניתנת לשימוש במידול.",
|
||||
"refused.faces_apart": "הפאות שלו אינן נפגשות בצמידות מספקת לשימוש במידול.",
|
||||
"refused.intersect": "לא ניתן היה לחתוך עם {body}.",
|
||||
"refused.join": "לא ניתן היה לחבר אל {body}.",
|
||||
"refused.repair": "תיקון חלק",
|
||||
"refused.show_face": "הצגת הפאה",
|
||||
"render.adaptive_absolute": "רצפת שגיאת RGB מוחלטת",
|
||||
"render.adaptive_coverage": "יעד שגיאת כיסוי",
|
||||
"render.adaptive_enable": "הקצאת דגימות באמצעות דגימות גישוש עצמאיות",
|
||||
@@ -3948,11 +4054,20 @@
|
||||
"the section's profiles must be in one sketch": "הפרופילים של החתך חייבים להיות בסקיצה אחת",
|
||||
"the two origins are on the same component": "שתי הראשיות נמצאות על אותו רכיב",
|
||||
"thread.automatic_size": "גודל אוטומטי",
|
||||
"thread.chamfer_depth_needed": "נדרש לצורה זו",
|
||||
"thread.designation": "סימון",
|
||||
"thread.direction": "כיוון ההברגה",
|
||||
"thread.drill_range": "מקדח יצרן המברז הוא {drill} מ\"מ. הקוטר הקטן התקני של התבריג הפנימי הוא {minor} מ\"מ. מתקבל מקדח מ-{low} עד {high} מ\"מ.",
|
||||
"thread.drill_range_custom": "המקדח הוא הקוטר הגדול פחות הפסיעה, {drill} מ\"מ. הקוטר הקטן התקני של התבריג הפנימי הוא {minor} מ\"מ. מתקבל מקדח מ-{low} עד {high} מ\"מ.",
|
||||
"thread.drill_range_taper": "מקדח יצרן המברז הוא {drill} מ\"מ. הקוטר הקטן התקני בסוף התבריג הוא {minor} מ\"מ. מתקבל מקדח מ-{low} עד {high} מ\"מ.",
|
||||
"thread.end_square": "ישר",
|
||||
"thread.fade_short": "1.25 P (ISO 3508)",
|
||||
"thread.fade_standard": "2.5 P (ISO 3508)",
|
||||
"thread.fits_the_face": "מתאים לפאה, למשל {example}",
|
||||
"thread.form_bspp": "תבריג צנרת G (ISO 228-1)",
|
||||
"thread.form_bspt": "תבריג צנרת קוני R, Rc (ISO 7-1)",
|
||||
"thread.form_npt": "תבריג צנרת קוני NPT (ASME B1.20.1)",
|
||||
"thread.form_rp": "תבריג צנרת פנימי מקביל Rp (ISO 7-1)",
|
||||
"thread.iso_metric": "ISO מטרי",
|
||||
"thread.left_hand": "שמאלית",
|
||||
"thread.right_hand": "ימנית",
|
||||
"thread.select_size": "בחירת גודל",
|
||||
"timeline.delete_after_marker_one": "מחיקת פיצ'ר אחד אחרי סמן ציר הזמן",
|
||||
"timeline.delete_after_marker_other": "מחיקת {count} פיצ'רים אחרי סמן ציר הזמן",
|
||||
@@ -4126,6 +4241,7 @@
|
||||
"ui.agent_chat_auth_terminal": "יש להריץ את זה במסוף ואז ללחוץ שוב על התחברות:",
|
||||
"ui.agent_chat_auth_title": "התחברות לסוכן",
|
||||
"ui.agent_chat_cancel": "ביטול",
|
||||
"ui.agent_chat_cancel_timeout": "הסוכן לא הגיב לבקשת העצירה ונותק. יש להתחבר מחדש כדי להמשיך.",
|
||||
"ui.agent_chat_cannot_start": "מותקן, אך אינו מופעל: {error}",
|
||||
"ui.agent_chat_changes": "שינויים: {n}, צעד ביטול אחד",
|
||||
"ui.agent_chat_changes_n": "{n} שינויים",
|
||||
@@ -4203,6 +4319,7 @@
|
||||
"ui.agent_chat_no_images": "הסוכן הזה לא מקבל תמונות",
|
||||
"ui.agent_chat_no_picture": "אין תמונה בלוח",
|
||||
"ui.agent_chat_no_tool_call": "המודל לא השתמש בכלים של SolidCraft, ולכן התכן לא השתנה. יש לבחור מודל שתומך בקריאה לכלים; ראו „מודלים מקומיים” במדריך צ'אט הסוכן.",
|
||||
"ui.agent_chat_node_too_old": "גרסת Node.js {version} ישנה מדי עבור סוכן זה. יש להתקין Node.js 20.10 ומעלה, ואז ללחוץ על רענון.",
|
||||
"ui.agent_chat_none_found": "לא נמצא סוכן קוד. יש להתקין אחד מאלה ואז ללחוץ על רענון:",
|
||||
"ui.agent_chat_none_found_short": "לא נמצא סוכן",
|
||||
"ui.agent_chat_not_installed": "לא מותקן",
|
||||
@@ -4422,6 +4539,8 @@
|
||||
"ui.centerline": "קו מרכז",
|
||||
"ui.chain": "שרשרת",
|
||||
"ui.chamfer": "פאזה",
|
||||
"ui.chamfer_angle": "זווית פאזה",
|
||||
"ui.chamfer_depth": "עומק פאזה",
|
||||
"ui.chamfer_type": "סוג פאזה",
|
||||
"ui.change_workspace": "החלפת סביבת עבודה",
|
||||
"ui.circle": "מעגל",
|
||||
@@ -4452,10 +4571,16 @@
|
||||
"ui.colour_vision_sample": "דוגמה",
|
||||
"ui.colour_vision_tritan": "טריטן",
|
||||
"ui.colour_vision_tritan_tip": "לתחושת כחול חלשה או חסרה",
|
||||
"ui.comb_length": "אורך המסרק",
|
||||
"ui.comb_length_tip": "אורך השיניים: כפולה של האורך האוטומטי, שבו השן הארוכה ביותר היא חמישית מגודל העקומות המוצגות.",
|
||||
"ui.comb_teeth": "שיניים",
|
||||
"ui.comb_teeth_tip": "כמה שיניים יש לכל מסרק, במרווחים שווים לאורך העקומה. יותר שיניים מראות בפירוט רב יותר איך העקמומיות משתנה.",
|
||||
"ui.combine": "שילוב",
|
||||
"ui.coming_later": "בקרוב",
|
||||
"ui.command_done": "{command} הושלם",
|
||||
"ui.command_palette_hint": "יש להקליד פקודה, למשל {example} — או ללחוץ S לחיפוש",
|
||||
"ui.components_from_bodies_hint": "יצירת רכיב לכל גוף שנבחר, בשם הגוף, תחת הרכיב הפעיל.",
|
||||
"ui.components_from_bodies_required": "יש לבחור גוף אחד לפחות.",
|
||||
"ui.concentric": "משותף מרכז",
|
||||
"ui.cone": "חרוט",
|
||||
"ui.conic_curve": "עקום חרוטי",
|
||||
@@ -4474,11 +4599,24 @@
|
||||
"ui.construction.ribbon_label": "קווי עזר",
|
||||
"ui.construction.side_hint": "ישר עזר: לחיצה על הצד שאליו הקו נמשך; לחיצה ימנית להתחלה מחדש",
|
||||
"ui.context_menu": "תפריט הקשר",
|
||||
"ui.continuity_crease": "שבר",
|
||||
"ui.continuity_curvature": "עקמומיות",
|
||||
"ui.continuity_curvature_change": "שינוי עקמומיות",
|
||||
"ui.continuity_curvature_change_tip": "שינוי עקמומיות (G3): גם העקמומיות משתנה בצורה חלקה לאורך התפר.",
|
||||
"ui.continuity_curvature_tip": "רציף בעקמומיות (G2): ההשתקפויות עוברות בלי שבר.",
|
||||
"ui.continuity_g0": "G0 שבר",
|
||||
"ui.continuity_g1": "G1 משיק",
|
||||
"ui.continuity_g2": "G2 עקמומיות",
|
||||
"ui.continuity_gap": "פער",
|
||||
"ui.continuity_position": "מיקום",
|
||||
"ui.continuity_position_tip": "מיקום (G0): המשטחים נפגשים, עם שבר במקום המפגש.",
|
||||
"ui.continuity_smooth_curvature_change": "שינוי עקמומיות חלק",
|
||||
"ui.continuity_smooth_curvature_change_tip": "שינוי עקמומיות חלק (G4): גם האופן שבו העקמומיות משתנה ממשיך בצורה חלקה לאורך התפר.",
|
||||
"ui.continuity_tangent": "משיק",
|
||||
"ui.continuity_tangent_tip": "משיק (G1): אין שבר, אך העקמומיות קופצת לאורך התפר.",
|
||||
"ui.copy": "העתקה",
|
||||
"ui.copy_details": "העתקת פרטים",
|
||||
"ui.copy_details_tip": "העתקת ההודעה המלאה, כדי לחפש אותה או לדווח עליה.",
|
||||
"ui.corner": "פינה",
|
||||
"ui.corners": "פינות",
|
||||
"ui.cosmetic_thread_markings": "סימוני הברגה קוסמטיים",
|
||||
@@ -4596,8 +4734,10 @@
|
||||
"ui.edit_sketch_empty": "אין סקיצות לעריכה",
|
||||
"ui.ellipse": "אליפסה",
|
||||
"ui.emboss_wrap_note": "פרופילי קווים ניצבים המשיקים לגליל נתמך נכרכים אוטומטית. יש לבחור דופן כדי לקבוע יעד.",
|
||||
"ui.end": "סוף",
|
||||
"ui.end_radius": "רדיוס סיום",
|
||||
"ui.end_scale": "קנה מידה בסיום",
|
||||
"ui.ends": "קצוות",
|
||||
"ui.entities": "ישויות",
|
||||
"ui.equal": "שווה",
|
||||
"ui.equation_end": "סוף",
|
||||
@@ -4617,6 +4757,7 @@
|
||||
"ui.export_ellipsis_keymap": "ייצוא…",
|
||||
"ui.export_keymap_tip": "מקשים, מועדפי תיבת S וסרגל הכלים, כקובץ",
|
||||
"ui.extend": "הארכה",
|
||||
"ui.extent": "גבול",
|
||||
"ui.extent_type": "סוג גבול",
|
||||
"ui.extract_component": "חילוץ לקובץ…",
|
||||
"ui.extract_design_changed": "התכן השתנה בזמן בחירת הקובץ. יש להפעיל שוב את חילוץ לקובץ.",
|
||||
@@ -4625,11 +4766,39 @@
|
||||
"ui.extrude_auto_to_object": "שליפה עד יעד שנבחר",
|
||||
"ui.extrude_auto_to_object_tip": "בשליפה, כשנבחר פרופיל, בחירת פאה, קודקוד או מישור מקביל מחוץ למישור שלו מעבירה את התחום ל\"עד אובייקט\"",
|
||||
"ui.factor": "מקדם",
|
||||
"ui.fade": "דעיכה",
|
||||
"ui.fade_length": "אורך דעיכה",
|
||||
"ui.feature_history": "ציר הזמן",
|
||||
"ui.feature_options": "אפשרויות פיצ'ר",
|
||||
"ui.features": "פיצ'רים",
|
||||
"ui.file": "קובץ",
|
||||
"ui.fillet": "עיגול פינה",
|
||||
"ui.fillet_corner_ball": "כדור",
|
||||
"ui.fillet_corner_ball_tip": "חלק מספירת הכדור המתגלגל, משיק (G1) לכל עיגול.",
|
||||
"ui.fillet_corner_curvature_tip": "עקמומיות (G2): טלאי חלק. הפינה נסוגה לאורך כל קצה במרחק הנסיגה (פי {factor} מהרדיוס אלא אם תקלידו ערך) ומתמלאת בטלאי, כך שההשתקפויות עוברות בה בלי שבר.",
|
||||
"ui.fillet_corner_needs_constant": "פינות עם טלאי חלק (G2) דורשות רדיוס קבוע והתאמת רדיוס מדויקת.",
|
||||
"ui.fillet_corner_patch": "טלאי חלק",
|
||||
"ui.fillet_corner_patch_tip": "רציף בעקמומיות (G2): הפינה נסוגה לאורך כל קצה ומתמלאת בטלאי, כך שההשתקפויות עוברות בה בלי שבר.",
|
||||
"ui.fillet_corner_tangent_tip": "משיק (G1): פינת כדור מתגלגל, חלק מספירת הכדור המתגלגל, משיק לכל עיגול.",
|
||||
"ui.fillet_error_corner": "לא ניתן היה לבנות כאן את הפינה החלקה. נסו נסיגה אחרת, או פינות משיקות.",
|
||||
"ui.fillet_error_failed": "לא ניתן היה לבנות את העיגול הזה על הקצוות האלה. נסו רדיוס אחר, או עגלו פחות קצוות בבת אחת.",
|
||||
"ui.fillet_error_setback_short": "הנסיגה קצרה מדי עבור הטלאי של פינה זו: הוא נבנה החל מ-{least}. נסו נסיגה ארוכה יותר.",
|
||||
"ui.fillet_error_too_large": "הרדיוס גדול מדי עבור הפאות שליד הקצוות האלה. נסו רדיוס קטן יותר, או בחרו ב„גבולות הפאות” את „הרדיוס הגדול ביותר”.",
|
||||
"ui.fillet_fit": "גבולות הפאות",
|
||||
"ui.fillet_fit_exact": "רדיוס מדויק",
|
||||
"ui.fillet_fit_needs_constant": "התאמת רדיוס דורשת רדיוס קבוע.",
|
||||
"ui.fillet_fit_reduce": "הרדיוס הגדול ביותר",
|
||||
"ui.fillet_fit_taper": "רדיוס מוצר",
|
||||
"ui.fillet_fit_tip": "כשהרדיוס גדול ממה שהפאות שליד הקצה מאפשרות.\nרדיוס מדויק: הרדיוס כפי שניתן, או ללא עיגול והודעה שמסבירה למה.\nהרדיוס הגדול ביותר: כל קצה מעוגל ברדיוס הגדול ביותר שנכנס.\nרדיוס מוצר: הרדיוס נשמר, והעיגול מוצר רק היכן שהפאות נגמרות.",
|
||||
"ui.fillet_options_tip": "שרשרת משיקה: לעגל גם את הקצוות שממשיכים בחלקות מהקצוות שנבחרו.\nתצוגה מקדימה: להציג את התוצאה בתצוגה בזמן העריכה.",
|
||||
"ui.fillet_setback": "נסיגה",
|
||||
"ui.fillet_setback_hint": "{factor} × רדיוס",
|
||||
"ui.fillet_setback_least": "יותר מ-{least}",
|
||||
"ui.fillet_setback_tip": "היכן מתחיל טלאי הפינה החלק: המרחק לאורך כל קצה מהפינה. ברירת המחדל היא פי {factor} מהרדיוס. עליה להיות גדולה מהנסיגה המזערית, שבה העיגולים נפגשים בלעדיה: הרדיוס כשהפאות ניצבות זו לזו.",
|
||||
"ui.fillet_smooth_radius": "שינוי רדיוס חלק",
|
||||
"ui.fillet_type_choices_tip": "רדיוס קבוע: רדיוס אחד לכל אורך הקצה.\nאורך מיתר: רוחב העיגול נשאר זהה, בכל זווית שבה הפאות נפגשות.\nרדיוס משתנה: רדיוס בכל קצה, ועוד לאורך הקצה אם מוסיפים.\nרציף בעקמומיות: חתך בלי שבר בהשתקפויות היכן שהוא פוגש פאות מישוריות (G2).\nספליין: הרדיוס לאורך הקצה עוקב אחרי רדיוסי בקרה שאתם קובעים.",
|
||||
"ui.fillet_type_curvature": "רציף בעקמומיות",
|
||||
"ui.fillet_type_tip": "החתך לרוחב הקצה. רציף בעקמומיות: חתך רציף בעקמומיות (G2) בין פאות מישוריות, כך שההשתקפויות עוברות מהפאות אל העיגול בלי שבר.",
|
||||
"ui.filter_commands_or_keys": "סינון פקודות או מקשים",
|
||||
"ui.findInTimeline": "איתור בציר הזמן",
|
||||
"ui.findInWindow": "איתור בחלון",
|
||||
@@ -4761,6 +4930,7 @@
|
||||
"ui.history_sketch_listed_under": "מופיעה תחת {feature}",
|
||||
"ui.history_title": "ציר הזמן",
|
||||
"ui.history_toggle_names": "הצגה או הסתרה של שמות הפיצ'רים",
|
||||
"ui.history_waits_for_editor": "ביטול, ביצוע מחדש וציר הזמן ממתינים עד שעורך הפעולה ייפתח",
|
||||
"ui.hole": "חור",
|
||||
"ui.hole_tap_type": "סוג הברגת חור",
|
||||
"ui.hole_type": "סוג חור",
|
||||
@@ -4778,6 +4948,7 @@
|
||||
"ui.inspect": "בדיקה",
|
||||
"ui.instances": "מופעים",
|
||||
"ui.intersect": "חיתוך משותף",
|
||||
"ui.iso_4753": "ISO 4753",
|
||||
"ui.isolate": "בידוד",
|
||||
"ui.join": "איחוד",
|
||||
"ui.joint_play": "▶ הפעלה",
|
||||
@@ -4880,6 +5051,7 @@
|
||||
"ui.part_size_needs_a_part": "בחרו חלק סטנדרטי שהוכנס (בורג, אום, דסקית או פין) כדי לשנות את גודלו. כדי להגדיל או להקטין גוף, השתמשו בשינוי קנה מידה.",
|
||||
"ui.part_size_shared": "בונה מחדש את החלק ומחליף פיצ'רים שנוספו. כל המופעים של רכיב זה מתעדכנים.",
|
||||
"ui.paste": "הדבקה",
|
||||
"ui.patch_continuity_corner_tip": "הטלאי פוגש את הפאות שסביבו ברציפות שנבחרה לאורך כל צלע. בפינה שבה הפאות האלה נפגשות זו עם זו בעקמומיות שונה, הוא נשאר משיק (G1) שם.",
|
||||
"ui.path": "מסלול",
|
||||
"ui.pattern_object_type_tip": "גופים: צורות מלאות, נבחרות בתצוגה או בעץ התכן; כל אחד מועתק בשלמותו.\nפיצ'רים: שלבים בציר הזמן, כמו שליפה או חור, נבחרים בציר הזמן או על פאה שהם יצרו; השלב חוזר על עצמו.\nסקיצה אינה אף אחד מהם: חזרו על העקומות שלה בעזרת המערך או השיקוף של הסקיצה עצמה, או בצעו לה שליפה קודם.",
|
||||
"ui.pattern_on_path": "מערך לאורך מסלול",
|
||||
@@ -5056,6 +5228,8 @@
|
||||
"ui.sketch_intersect_geometry": "גאומטריה",
|
||||
"ui.sketch_intersect_projection_link": "קישור הטלה",
|
||||
"ui.sketch_intersect_selection_filter": "מסנן בחירה",
|
||||
"ui.sketch_keys_only": "להשאיר את מקשי המודל מחוץ לסקיצות",
|
||||
"ui.sketch_keys_only_tip": "בזמן עריכת סקיצה פועלים רק מקשי סקיצה ומקשים שפועלים בכל מקום; מקש של המודל כמו E או F לא עושה דבר, כך שמקש שנלחץ בטעות לא יסגור סקיצה כבדה.",
|
||||
"ui.sketch_modifiers": "מקשי שינוי בסקיצה",
|
||||
"ui.sketch_project_geometry": "גאומטריה",
|
||||
"ui.sketch_report.all_constrained": "הכול מאולץ במלואו.",
|
||||
@@ -5073,7 +5247,7 @@
|
||||
"ui.slicer_no_response": "{slicer} לא הגיב בזמן. הקובץ כבר שמור ב־{path}: יש לשלוח אותו שוב אחרי שהסלייסר יופעל.",
|
||||
"ui.slicer_resend_hint": "שליחה חוזרת מחליפה את הקובץ שפתוח בסלייסר.",
|
||||
"ui.slicer_single_instance": "פתיחת קבצים בסלייסר שכבר פועל",
|
||||
"ui.slicer_single_instance_tip": "PrusaSlicer, SuperSlicer ו־Cura טוענים את הקובץ בחלון הפתוח במקום להפעיל חלון נוסף. סלייסרים אחרים מופעלים כרגיל. PrusaSlicer 3.0 עושה זאת תמיד: המפעיל שלו לא יכול לקבל קובץ בדרך אחרת.",
|
||||
"ui.slicer_single_instance_tip": "לסלייסרים שטוענים קובץ לחלון שכבר פועל.",
|
||||
"ui.slicer_starting": "מפעיל את {slicer}…",
|
||||
"ui.slicer_still_starting": "עדיין ממתין להפעלת {slicer}. שום דבר נוסף לא נשלח.",
|
||||
"ui.slicer_stl_fallback": "לא ניתן היה לכתוב את קובץ ה־3MF, ולכן זהו קובץ STL.",
|
||||
@@ -5218,13 +5392,13 @@
|
||||
"update.available": "SolidCraft {version} זמין",
|
||||
"update.button": "עדכון",
|
||||
"update.button_version": "עדכון ל־{version}",
|
||||
"update.check_automatically": "בדיקת עדכונים אוטומטית",
|
||||
"update.check_automatically": "בדיקה בהפעלה ופעם ביום",
|
||||
"update.check_for_updates": "בדיקת עדכונים",
|
||||
"update.check_now": "בדיקה עכשיו",
|
||||
"update.check_unavailable": "בדיקת עדכונים אינה זמינה עבור התקנה זו.",
|
||||
"update.checking": "בבדיקה…",
|
||||
"update.copy": "העתקה",
|
||||
"update.dmg_opened": "תמונת הדיסק פתוחה ואומתה: יש לגרור את SolidCraft לתיקיית היישומים במקום העותק הישן, ואז להפעיל מחדש את SolidCraft.",
|
||||
"update.dmg_opened": "תמונת הדיסק פתוחה ואומתה: יש לצאת מ-SolidCraft, לגרור את SolidCraft החדש לתיקיית היישומים במקום העותק הישן, ואז לפתוח את SolidCraft מחדש.",
|
||||
"update.download": "הורדה",
|
||||
"update.download_open": "הורדה ופתיחה",
|
||||
"update.downloading": "הורדה ואימות… {done} מתוך {total} MB",
|
||||
|
||||
+78
-3
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "Pravokutni",
|
||||
"Rectangular Tube": "Pravokutna cijev",
|
||||
"Reference face": "Referentno lice",
|
||||
"Reference plane": "Referentna ravnina",
|
||||
"Relief Depth": "Dubina rasterećenja",
|
||||
"Relief Width": "Širina rasterećenja",
|
||||
"Remove Tools": "Ukloni alate",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "Presjek",
|
||||
"Section (optional)": "Presjek (neobavezno)",
|
||||
"Section position": "Položaj presjeka",
|
||||
"Sections": "Presjeci",
|
||||
"Sections (one sketch each)": "Presjeci (po jedna skica)",
|
||||
"Select Other": "Odaberi drugo",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Odaberi drugo: držite lijevu tipku mirno na modelu (ili desni klik › Odaberi drugo…) kako biste prikazali sve ispod pokazivača; ponovno kliknite na isto mjesto kako biste odabrali sljedeći element. Dvaput kliknite srednjom tipkom za Početni prikaz (Postavke › Navigacija).",
|
||||
@@ -859,6 +861,7 @@
|
||||
"cmd.component_extract": "Izdvoji u datoteku",
|
||||
"cmd.component_finish_edit": "Završi uređivanje",
|
||||
"cmd.component_from_bodies": "Izradi komponente iz tijela",
|
||||
"cmd.component_hide_history": "Sakrij vremensku traku komponente",
|
||||
"cmd.component_insert_design": "Umetni dizajn",
|
||||
"cmd.component_links": "Popis veza",
|
||||
"cmd.component_list": "Popis komponenti",
|
||||
@@ -875,6 +878,7 @@
|
||||
"cmd.component_rename": "Preimenuj komponentu",
|
||||
"cmd.component_replace_with_link": "Zamijeni vezom",
|
||||
"cmd.component_revert_position": "Vrati položaj",
|
||||
"cmd.component_show_history": "Prikaži vremensku traku komponente",
|
||||
"cmd.component_unlock": "Otključaj sadržaj",
|
||||
"cmd.component_update_links": "Ažuriraj veze",
|
||||
"cmd.config_activate": "Aktiviraj konfiguraciju",
|
||||
@@ -885,6 +889,7 @@
|
||||
"cmd.config_rule": "Pravilo konfiguracije",
|
||||
"cmd.config_rules": "Pravila konfiguracije",
|
||||
"cmd.config_table": "Prikaži tablicu konfiguracija",
|
||||
"cmd.construct.plane.axis_point": "Ravnina kroz os i točku",
|
||||
"cmd.construct_axis_edge": "Os kroz brid",
|
||||
"cmd.construct_axis_normal_to_face": "Os okomita na lice",
|
||||
"cmd.construct_axis_through_cylinder": "Os kroz valjak/stožac/torus",
|
||||
@@ -933,6 +938,11 @@
|
||||
"cmd.explode_show": "Prikaži rastavljeni prikaz",
|
||||
"cmd.expr_evaluate": "Izračunaj izraz",
|
||||
"cmd.face_delete": "Izbriši lice",
|
||||
"cmd.file.export_3mf": "Izvezi 3MF",
|
||||
"cmd.file.export_obj": "Izvezi OBJ",
|
||||
"cmd.file.export_step": "Izvezi STEP",
|
||||
"cmd.file.export_stl": "Izvezi STL",
|
||||
"cmd.file.export_urdf": "Izvezi URDF",
|
||||
"cmd.file_export": "Izvoz",
|
||||
"cmd.file_insert_mesh": "Umetni mrežu",
|
||||
"cmd.file_insert_step": "Umetni STEP",
|
||||
@@ -950,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "Omogući boje komponenti",
|
||||
"cmd.inspect_colored_components_disable": "Onemogući boje komponenti",
|
||||
"cmd.inspect_compare": "Usporedi tijela",
|
||||
"cmd.inspect_compare_picture": "Usporedi sliku",
|
||||
"cmd.inspect_continuity": "Analiza kontinuiteta",
|
||||
"cmd.inspect_curvature_comb": "Analiza češlja zakrivljenosti",
|
||||
"cmd.inspect_curvature_map": "Analiza mape zakrivljenosti",
|
||||
@@ -1040,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "Uredi oblak točaka",
|
||||
"cmd.pointcloud_insert": "Umetni oblak točaka",
|
||||
"cmd.pointcloud_list": "Oblaci točaka",
|
||||
"cmd.render.compare.divider": "Pomakni razdjelnik usporedbe renderiranja",
|
||||
"cmd.render.result.discard": "Odbaci rezultat renderiranja",
|
||||
"cmd.render.result.select": "Odaberi rezultat renderiranja",
|
||||
"cmd.render_aging_describe": "Slojevito starenje površine",
|
||||
"cmd.render_backends": "Pozadinski sustavi renderiranja",
|
||||
"cmd.render_batch": "Renderiraj sve kadrove",
|
||||
@@ -1254,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "Pomakni oznaku vraćanja",
|
||||
"cmd.timeline_stop": "Zaustavi vremensku traku",
|
||||
"cmd.timeline_suppress": "Potisni značajku",
|
||||
"cmd.ui.assistant": "Prikaži ili sakrij Asistenta",
|
||||
"cmd.view.render_browser": "Stablo renderiranja",
|
||||
"cmd.view.render_dock": "Premjesti panel renderiranja",
|
||||
"cmd.view.render_focus": "Fokusni prikaz renderiranja",
|
||||
@@ -1288,9 +1303,11 @@
|
||||
"cmd.view_tool": "Alat za navigaciju",
|
||||
"cmd.view_top": "Pogled odozgo",
|
||||
"cmd.view_visual_style": "Vizualni stil",
|
||||
"color.blue": "Plava",
|
||||
"color.copy": "Kliknite za kopiranje vrijednosti boje",
|
||||
"color.edit": "Kliknite za uređivanje boje",
|
||||
"color.gamma": "Prikaz vrijednosti boje u gama prostoru 0-255",
|
||||
"color.green": "Zelena",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "Unesite #rrggbb ili #rrggbbaa; šest znamenki zadržava neprozirnost",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1298,6 +1315,7 @@
|
||||
"color.linear": "Prikaz vrijednosti boje u linearnom prostoru 0-1",
|
||||
"color.recent": "Nedavne boje",
|
||||
"color.recent_help": "Ovdje se pojavljuju boje koje odaberete u bilo kojem polju za boju",
|
||||
"color.red": "Crvena",
|
||||
"color.saturation": "Zasićenost (postotak)",
|
||||
"color.saturation_value": "Zasićenost i vrijednost",
|
||||
"color.selected": "Odabrana boja",
|
||||
@@ -1305,6 +1323,8 @@
|
||||
"color.step_up": "Povećaj za korak",
|
||||
"color.value": "Vrijednost, svjetlina (postotak)",
|
||||
"config.column_filter_empty": "Nema odgovarajućih stupaca",
|
||||
"config.rule_parameter_columns": "Navedeni su samo parametri koji imaju stupac u tablici. Dodajte stupac pomoću Dodaj stupac (+) kako biste parametar mogli upotrijebiti ovdje.",
|
||||
"config.rule_suppression_columns": "Navedene su samo značajke koje imaju stupac Potisni u tablici. Dodajte stupac pomoću Dodaj stupac (+) kako biste ovdje mogli upravljati značajkom.",
|
||||
"contact.components_count": "Komponente: {count}",
|
||||
"contact.enabled_label": "omogućeno",
|
||||
"coupling.gear_1": "Zupčanik 1",
|
||||
@@ -1353,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "Odaberite tijela ili značajke: skica nije ni jedno ni drugo. Ponovite njezine krivulje pomoću alata Niz ili Zrcali u samoj skici ili je najprije ekstrudirajte.",
|
||||
"dialog.select": "↖ Odaberi",
|
||||
"dialog.selected_count": "↖ Odabrano: {count}",
|
||||
"dialog.selection_constraint": "Ograničenje {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · krivulja {id}",
|
||||
"dialog.selection_guidance": "Odaberite ulaz kako biste ga promijenili. Kliknite odabrane objekte kako biste ih uklonili; Ctrl/Cmd-klik ih također uključuje i isključuje. Pogledajte pretpregled, a zatim pritisnite U redu.",
|
||||
"dialog.selection_midpoint": "Središnja točka · {id}",
|
||||
"dialog.selection_profile": "{sketch} · profil {number}",
|
||||
"dialog.selection_step": "{step} od {total}: Odaberite {input}",
|
||||
"dialog.step": "Korak",
|
||||
"document.changed_on_disk": "{name} je promijenjen na disku",
|
||||
@@ -1436,6 +1460,7 @@
|
||||
"help.construct.axis.two_points": "Izrađuje pomoćnu os kroz dvije točke, vrhove ili točke skice. Upotrijebite je za os koja prolazi između dvaju poznatih položaja.",
|
||||
"help.construct.plane.along_path": "Izrađuje pomoćnu ravninu okomitu na brid ili krivulju, na položaju duž njih. Upotrijebite je za skiciranje profila za povlačenje ili presjeka putanje.",
|
||||
"help.construct.plane.at_angle": "Izrađuje pomoćnu ravninu zakrenutu za kut u odnosu na ravninu ili lice, oko osi ili brida. Upotrijebite je za nagnute skice, primjerice kutnog nosača.",
|
||||
"help.construct.plane.axis_point": "Izrađuje pomoćnu ravninu koja sadrži os i točku izvan te osi. Odaberite os ishodišta ili pomoćnu os, ravni brid ili liniju skice, pa vrh, točku skice ili pomoćnu točku. Točka na osi ne može odrediti jedinstvenu ravninu.",
|
||||
"help.construct.plane.midplane": "Izrađuje pomoćnu ravninu na pola puta između dviju ravnina ili ravnih lica, ili ravninu koja raspolavlja kut kad se one sijeku pod kutom. Upotrijebite je kao ravninu simetrije za zrcaljenje.",
|
||||
"help.construct.plane.offset": "Izrađuje pomoćnu ravninu paralelnu s ravninom ili ravnim licem, na određenoj udaljenosti ili kroz točku. Upotrijebite je za skiciranje na zadanoj visini iznad lica.",
|
||||
"help.construct.plane.perpendicular": "Izrađuje pomoćnu ravninu koja sadrži liniju ili brid i okomita je na ravninu ili lice. Upotrijebite je za skiciranje uspravno na modelu duž brida.",
|
||||
@@ -1812,6 +1837,8 @@
|
||||
"joint_move.blocked_hint": "Provjerite vrstu spoja (npr. Kruti), učvršćenje, granice spoja i vrijednosti kojima upravljaju parametri.",
|
||||
"joint_move.blocked_title": "Ovaj dio nije moguće pomaknuti",
|
||||
"joint_move.coarse": "Korak rotacije blizu osi (°)",
|
||||
"joint_move.conflict": "Jedan spoj ovog dijela u sukobu je s ostalima: {joint}.",
|
||||
"joint_move.conflict_hint": "Uredite, potisnite ili izbrišite spoj koji je u sukobu kako bi ostali spojevi mogli postaviti dio.",
|
||||
"joint_move.distance": "Udaljenost od početka",
|
||||
"joint_move.done": "Gotovo",
|
||||
"joint_move.fine": "Korak rotacije daleko od osi (°)",
|
||||
@@ -1911,6 +1938,8 @@
|
||||
"motion.track_name": "Staza {number}",
|
||||
"nav.alias.help": "Shift+Alt+povlačenje lijevom tipkom orbitira, Shift+Alt+srednjom pomiče, Shift+Alt+desnom zumira; povlačenje srednjom tipkom također pomiče, a desnom orbitira",
|
||||
"nav.blender.help": "Povlačenje srednjom tipkom orbitira, Shift+srednja pomiče, Ctrl+srednja zumira; povlačenje desnom tipkom ne dira kameru",
|
||||
"nav.creo.help": "Povlačenje srednjom tipkom orbitira, Shift+srednja pomiče, Ctrl+srednja uz okomito povlačenje zumira (prema dolje za približavanje); kotačić također zumira",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "Miš i 3D miš rotiraju kroz oba pola i čuvaju nagib. Privlačenje osi tipkom Shift isključeno je.",
|
||||
"nav.free_orbit.label": "Slobodno orbitiranje",
|
||||
"nav.free_orbit.snap_help": "Miš i 3D miš rotiraju kroz oba pola i čuvaju nagib. Držite Shift tijekom povlačenja za orbitiranje kako biste privukli smjer pogleda i nagib.",
|
||||
@@ -2063,12 +2092,19 @@
|
||||
"parts.family.button_head_cap_screw": "Vijak s poluokruglom glavom i unutarnjim šesterokutom",
|
||||
"parts.family.dowel_pin": "Cilindrični zatik",
|
||||
"parts.family.hex_bolt": "Vijak sa šesterokutnom glavom",
|
||||
"parts.family.hex_cap_screw_inch": "Vijak sa šesterokutnom glavom (inč)",
|
||||
"parts.family.hex_nut": "Šesterokutna matica",
|
||||
"parts.family.hex_nut_inch": "Šesterokutna matica (inč)",
|
||||
"parts.family.set_screw": "Bezglavi vijak s unutarnjim šesterokutom, ravni vrh",
|
||||
"parts.family.socket_head_cap_screw": "Vijak s cilindričnom glavom i unutarnjim šesterokutom",
|
||||
"parts.family.washer": "Obična podloška",
|
||||
"parts.family.washer_sae": "Obična podloška, SAE (uska)",
|
||||
"parts.family.washer_uss": "Obična podloška, USS (široka)",
|
||||
"parts.holes": "Provrti (neobavezno)",
|
||||
"parts.placement_hint": "odaberite jedan ili više provrta kako biste u svaki smjestili dio, ili U redu postavlja dio u ishodište",
|
||||
"parts.system": "Sustav jedinica",
|
||||
"parts.system.inch": "Inčni",
|
||||
"parts.system.metric": "Metrički",
|
||||
"pattern.alignment_2": "Poravnanje 2",
|
||||
"pattern.alignment_hint": "Broj uključuje izvornik. Središte ravnomjerno raspoređuje kopije; Obrni bira dodatnu stranu za parne ukupne brojeve.",
|
||||
"pattern.direction_1": "Smjer 1",
|
||||
@@ -2130,6 +2166,8 @@
|
||||
"prefs.button_left": "Povlačenje lijevom tipkom",
|
||||
"prefs.button_middle": "Povlačenje srednjom tipkom",
|
||||
"prefs.button_right": "Povlačenje desnom tipkom",
|
||||
"prefs.carousel_next": "Sljedeće",
|
||||
"prefs.carousel_previous": "Prethodno",
|
||||
"prefs.choose_font": "Odaberi datoteku fonta…",
|
||||
"prefs.click": "Klik",
|
||||
"prefs.contact_shading": "Meko sjenčanje dodira",
|
||||
@@ -2142,6 +2180,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Zakrivljene plohe",
|
||||
"prefs.curves": "Zakrivljene plohe",
|
||||
"prefs.cut_preview_tint": "Prikaži uklonjeni materijal u crvenoj boji",
|
||||
"prefs.cut_preview_tint_help": "Pregled ispunjava materijal koji značajka uklanja i oblik koji zamjenjuje crvenom prozirnom sjenom. Kada je isključeno, te su ispune potpuno prozirne i crtaju se samo njihovi obrisi.",
|
||||
"prefs.dark_parts_shading": "Zadrži oblik tamnih dijelova",
|
||||
"prefs.dark_parts_shading_help": "Sjenča prikaz u linearnom svjetlu uz meko dopunsko svjetlo neba, tako da tamna lica zadrže gradijent umjesto da pocrne",
|
||||
"prefs.dead_zone_note": "Šrafirana traka je mrtva zona: kretanje unutar nje se zanemaruje.",
|
||||
@@ -2200,6 +2240,8 @@
|
||||
"prefs.interface_language": "Jezik sučelja",
|
||||
"prefs.interface_note": "Kako SolidCraft izgleda i gdje su njegovi dijelovi.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_curved_roll_arrows": "Obrni zakrivljene strelice za valjanje",
|
||||
"prefs.invert_curved_roll_arrows_note": "Dvije zakrivljene strelice u kutu kocke valjaju prikaz unutar ravnine zaslona. Uključeno: strelica u smjeru kazaljke na satu valja prikaz suprotno od smjera kazaljke na satu, a strelica suprotno od smjera kazaljke na satu valja ga u smjeru kazaljke na satu. Isključeno: svaka strelica valja prikaz onako kako je zakrivljena.",
|
||||
"prefs.invert_navigator_arrows": "Obrni strelice za rotaciju kocke prikaza",
|
||||
"prefs.invert_navigator_arrows_note": "Strelice oko kocke orbitiraju kameru u smjeru u kojem pokazuju, kao u programima Fusion i Inventor: strelica s lijeve strane pokazuje udesno i zakreće kameru udesno. Isključeno: strelica okreće kocku prema strani na kojoj se nalazi, pa lijeva strelica zakreće kameru ulijevo. Postavka ne utječe na zakrivljene strelice za valjanje.",
|
||||
"prefs.keep_work_on_tab_switch": "Zadrži dijaloge i alate otvorenima pri prebacivanju između dizajna",
|
||||
@@ -2259,6 +2301,8 @@
|
||||
"prefs.placement": "Postavljanje",
|
||||
"prefs.prefer_free_sketch_points": "Daj prednost pomičnim točkama skice kad se točke preklapaju",
|
||||
"prefs.preview_choices": "Pregled vaših odabira",
|
||||
"prefs.preview_tint_new_faces": "Oboji novu plohu u pregledu",
|
||||
"prefs.preview_tint_new_faces_help": "Pregled značajke koja mijenja tijelo može nacrtati plohu koju dodaje u boji pregleda. Kada je isključeno, tijelo se crta kakvo jest.",
|
||||
"prefs.print_face_highlight": "Isticanje plohe ispisa",
|
||||
"prefs.print_face_highlight_tip": "Koliko se jako ploha ispisa tijela boji i obrubljuje u prikazu. 0 % ne prikazuje isticanje.",
|
||||
"prefs.profile_active": "Aktivni profil",
|
||||
@@ -2556,6 +2600,10 @@
|
||||
"render.base_color": "Osnovna boja · sRGB",
|
||||
"render.base_metallic": "Metalni udio",
|
||||
"render.base_opaque_only": "Ovaj uređivač čuva semantiku neprozirnih tijela. Za optičko propuštanje upotrijebite izričite kontrole stakla.",
|
||||
"render.batch_all_shots": "Svi kadrovi ({count})",
|
||||
"render.batch_empty": "Spremite kadar prije renderiranja svih kadrova.",
|
||||
"render.batch_limit": "Renderiranje svih kadrova ograničeno je na 32 kadra.",
|
||||
"render.batch_status": "U tijeku: {running} · Na čekanju: {waiting}",
|
||||
"render.bloom_enable": "Sjaj prikaza",
|
||||
"render.bloom_help": "Kreativni sjaj samo na izlazu iz pozitivnih linearnih svijetlih dijelova, raširen kroz tri normalizirana prolaza kutijastog filtra. Polumjer je u pikselima izlaza. Dodaje se u linearnoj svjetlosti prikaza prije sRGB-a; nije objektiv, volumetrijski efekt niti poboljšanje konvergencije. Sirovi EXR ostaje nepromijenjen.",
|
||||
"render.bloom_opaque": "Sjaj zahtijeva neprozirni izlaz. Prozirni PNG odbija se, a ne spljoštava tiho.",
|
||||
@@ -2666,6 +2714,13 @@
|
||||
"render.choose_look": "Najprije odaberite izgled u Izgledima",
|
||||
"render.chromatic_offset": "Kromatski pomak rubova",
|
||||
"render.clear_stage": "Ukloni spremljenu scenu",
|
||||
"render.compare_divider": "Razdjelnik usporedbe",
|
||||
"render.compare_live": "Uživo",
|
||||
"render.compare_live_preview": "Pregled uživo",
|
||||
"render.compare_render": "Renderiranje {number}",
|
||||
"render.compare_side": "Renderiranje {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} s · {renderer} · zabilježeno u reviziji {revision}",
|
||||
"render.compare_swap": "Zamijeni strane",
|
||||
"render.copy_request": "Kopiraj JSON",
|
||||
"render.cpu_aa_help": "Prostorni uzorci: više uzoraka poboljšava rubove i produljuje renderiranje.",
|
||||
"render.cpu_area_add": "Dodaj pravokutno CPU svjetlo",
|
||||
@@ -3415,6 +3470,10 @@
|
||||
"render.result_retained": "Prikazuje se posljednja dovršena slika; novije renderiranje još je nije zamijenilo.",
|
||||
"render.result_sequence": "Kontrolna točka",
|
||||
"render.result_title": "Rezultat renderiranja",
|
||||
"render.results_choose": "Zadržani rezultati",
|
||||
"render.results_label": "Renderiranje {number} · {shot} · {seconds} s",
|
||||
"render.results_stale_label": "{result} [zastarjelo]",
|
||||
"render.results_unnamed": "Slika",
|
||||
"render.return_design": "Natrag na Dizajn",
|
||||
"render.review_stale": "Ovaj pregled je zastario. Riješite ga ili ponovno provjerite prije primjene.",
|
||||
"render.ribbon_aging": "Starenje",
|
||||
@@ -3898,10 +3957,7 @@
|
||||
"the two origins are on the same component": "oba ishodišta nalaze se na istoj komponenti",
|
||||
"thread.automatic_size": "Automatska veličina",
|
||||
"thread.designation": "Oznaka",
|
||||
"thread.direction": "Smjer navoja",
|
||||
"thread.iso_metric": "ISO metrički",
|
||||
"thread.left_hand": "Lijevi",
|
||||
"thread.right_hand": "Desni",
|
||||
"thread.select_size": "Odaberite veličinu",
|
||||
"timeline.delete_after_marker_one": "Izbriši 1 značajku nakon oznake vraćanja",
|
||||
"timeline.delete_after_marker_other": "Izbriši značajke nakon oznake vraćanja: {count}",
|
||||
@@ -4075,6 +4131,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Pokrenite ovo u terminalu, a zatim pritisnite Poveži ponovno:",
|
||||
"ui.agent_chat_auth_title": "Prijavi se kod agenta",
|
||||
"ui.agent_chat_cancel": "Odustani",
|
||||
"ui.agent_chat_cancel_timeout": "Agent nije odgovorio na Zaustavi pa je veza s njim prekinuta. Ponovno se povežite kako biste nastavili.",
|
||||
"ui.agent_chat_cannot_start": "Instalirano, ali se ne pokreće: {error}",
|
||||
"ui.agent_chat_changes": "Promjene: {n}, jedan korak poništavanja",
|
||||
"ui.agent_chat_changes_n": "Promjene: {n}",
|
||||
@@ -4084,11 +4141,13 @@
|
||||
"ui.agent_chat_command": "Naredba agenta",
|
||||
"ui.agent_chat_connect": "Poveži",
|
||||
"ui.agent_chat_copy": "Kopiraj",
|
||||
"ui.agent_chat_copy_message": "Kopiraj poruku",
|
||||
"ui.agent_chat_ctx_bodies": "Tijela",
|
||||
"ui.agent_chat_ctx_elements": "Elementi",
|
||||
"ui.agent_chat_ctx_features": "Značajke",
|
||||
"ui.agent_chat_ctx_more": "+još {n}",
|
||||
"ui.agent_chat_ctx_other": "Ostalo",
|
||||
"ui.agent_chat_custom_heading": "Prilagođeni model",
|
||||
"ui.agent_chat_custom_hint": "Bilo koji program koji govori Agent Client Protocol na stdin i stdout. Ne pokreće ga nikakva ljuska.",
|
||||
"ui.agent_chat_denied": "Odbijeno",
|
||||
"ui.agent_chat_deny": "Odbij",
|
||||
@@ -4132,8 +4191,16 @@
|
||||
"ui.agent_chat_mode_all": "Sve",
|
||||
"ui.agent_chat_mode_ask": "Pitaj",
|
||||
"ui.agent_chat_mode_edits": "Izmjene",
|
||||
"ui.agent_chat_model_custom": "Koristi prilagođeni: {model}",
|
||||
"ui.agent_chat_model_no_tools": "{model} ne može koristiti SolidCraft alate, pa može samo razgovarati. Odaberite model s podrškom za alate.",
|
||||
"ui.agent_chat_model_rejected": "Agent nije prihvatio model {model}: {error}",
|
||||
"ui.agent_chat_model_search": "Pretraži modele…",
|
||||
"ui.agent_chat_model_tip": "Model koji agent koristi. Vaš odabir pamti se za ovog agenta.",
|
||||
"ui.agent_chat_models_failed": "Popis modela nije moguće učitati: {error}",
|
||||
"ui.agent_chat_models_heading": "Dostupni modeli",
|
||||
"ui.agent_chat_models_listing": "Učitavanje popisa modela…",
|
||||
"ui.agent_chat_models_none": "Krajnja točka ne navodi nijedan model",
|
||||
"ui.agent_chat_models_not_a_list": "Odgovor nije popis modela",
|
||||
"ui.agent_chat_new": "Novi razgovor",
|
||||
"ui.agent_chat_new_confirm": "Započeti novi razgovor? Trenutni će biti obrisan.",
|
||||
"ui.agent_chat_next_picture": "Sljedeća slika",
|
||||
@@ -4407,9 +4474,11 @@
|
||||
"ui.construction.contact_hint": "Tangenta: odaberite kružnicu/luk ili otvoreni spline/koniku, pa točku dodira (krivulje slobodnog oblika: krajnje točke)",
|
||||
"ui.construction.external_hint": "Tangenta: odaberite kružnicu ili luk, vanjsku točku, pa željeno rješenje",
|
||||
"ui.construction.free_hint": "Pomoćni pravac: kliknite dvije točke; desnim klikom počnite ispočetka",
|
||||
"ui.construction.normal_needs_length": "Beskonačna linija uvijek je pomoćna: upišite duljinu kako biste nacrtali običnu liniju",
|
||||
"ui.construction.point_hint": "Pomoćni pravac: kliknite točku kroz koju prolazi",
|
||||
"ui.construction.reference_hint": "Pomoćni pravac: odaberite ravnu liniju, pa točku; desnim klikom promijenite referencu",
|
||||
"ui.construction.ribbon_label": "Pomoćne",
|
||||
"ui.construction.side_hint": "Pomoćni pravac: kliknite stranu prema kojoj pravac ide; desnim klikom počnite ispočetka",
|
||||
"ui.context_menu": "Kontekstni izbornik",
|
||||
"ui.continuity_g0": "G0 pregib",
|
||||
"ui.continuity_g1": "G1 tangentno",
|
||||
@@ -4675,6 +4744,7 @@
|
||||
"ui.hideOrigin": "Sakrij ishodište",
|
||||
"ui.hidePlanes": "Sakrij ravnine",
|
||||
"ui.hideProfile": "Sakrij profil",
|
||||
"ui.hide_component_history": "Sakrij njezinu vremensku traku",
|
||||
"ui.hide_origin_by_default": "Zadano sakrij ishodište",
|
||||
"ui.hide_origin_note": "Novi dizajni počinju sa skrivenom mapom Ishodište. Dizajn zadržava vlastitu postavku Ishodišta kad ga prikažete ili sakrijete.",
|
||||
"ui.history_back": "Idi korak natrag",
|
||||
@@ -4682,6 +4752,7 @@
|
||||
"ui.history_edge_hint": "dvaput kliknite brid",
|
||||
"ui.history_first": "Idi na početak",
|
||||
"ui.history_forward": "Idi korak naprijed",
|
||||
"ui.history_hide_hidden": "Sakrij skrivene komponente",
|
||||
"ui.history_hide_names": "Sakrij nazive",
|
||||
"ui.history_last": "Idi na kraj",
|
||||
"ui.history_marker_tip": "Oznaka vraćanja: povucite kako biste vratili model unatrag; nove značajke dolaze ovdje",
|
||||
@@ -4691,10 +4762,12 @@
|
||||
"ui.history_play_button_show": "Prikaži tipku za reprodukciju",
|
||||
"ui.history_play_finish_action": "Završite trenutnu radnju prije reprodukcije vremenske trake",
|
||||
"ui.history_play_roll_back": "Pomaknite oznaku vraćanja unatrag prije reprodukcije",
|
||||
"ui.history_show_hidden": "Prikaži skrivene komponente",
|
||||
"ui.history_show_names": "Prikaži nazive",
|
||||
"ui.history_sketch_listed_under": "Navedeno ispod {feature}",
|
||||
"ui.history_title": "Vremenska traka",
|
||||
"ui.history_toggle_names": "Prikaži ili sakrij nazive značajki",
|
||||
"ui.history_waits_for_editor": "Poništi, Ponovi i vremenska traka čekaju dok se ne otvori uređivač značajke",
|
||||
"ui.hole": "Provrt",
|
||||
"ui.hole_tap_type": "Vrsta navoja provrta",
|
||||
"ui.hole_type": "Vrsta provrta",
|
||||
@@ -4975,6 +5048,7 @@
|
||||
"ui.showDimension": "Prikaži kotu",
|
||||
"ui.showOrigin": "Prikaži ishodište",
|
||||
"ui.showPlanes": "Prikaži ravnine",
|
||||
"ui.show_component_history": "Prikaži njezinu vremensku traku",
|
||||
"ui.show_constraints": "Ograničenja",
|
||||
"ui.show_construction_geometries": "Pomoćna geometrija",
|
||||
"ui.show_dimensions": "Kote",
|
||||
@@ -5201,6 +5275,7 @@
|
||||
"web.open_link": "Otvoriti povezani dizajn?",
|
||||
"web.open_link_confirm": "Postoje nespremljene promjene. Otvoriti povezani dizajn u novoj kartici?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ odaberi · Enter / Tab dovrši · Esc odbaci",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ odabir · Enter primjenjuje ovu vrijednost",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ odaberi · Enter primjenjuje ono što ste upisali · Esc odbaci",
|
||||
"⌘Enter continues on GitHub.": "⌘Enter nastavlja na GitHubu."
|
||||
}
|
||||
|
||||
+85
-4
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "Téglalap alakú",
|
||||
"Rectangular Tube": "Téglalap alakú cső",
|
||||
"Reference face": "Referencialap",
|
||||
"Reference plane": "Referenciasík",
|
||||
"Relief Depth": "Tehermentesítés mélysége",
|
||||
"Relief Width": "Tehermentesítés szélessége",
|
||||
"Remove Tools": "Szerszámok eltávolítása",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "Metszet",
|
||||
"Section (optional)": "Keresztmetszet (nem kötelező)",
|
||||
"Section position": "Szelvény helyzete",
|
||||
"Sections": "Keresztmetszetek",
|
||||
"Sections (one sketch each)": "Keresztmetszetek (egy-egy vázlat)",
|
||||
"Select Other": "Másik kijelölése",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Más kijelölése: tartsa nyomva a bal gombot egy helyen a modellen (vagy jobb kattintás › Más kijelölése…) a kurzor alatti elemek listázásához; kattintson újra ugyanarra a helyre a következő elem kijelöléséhez. A középső gomb dupla kattintása a kezdőnézetre vált (Beállítások › Navigáció).",
|
||||
@@ -859,6 +861,7 @@
|
||||
"cmd.component_extract": "Kivonás fájlba",
|
||||
"cmd.component_finish_edit": "Szerkesztés befejezése",
|
||||
"cmd.component_from_bodies": "Komponensek létrehozása testekből",
|
||||
"cmd.component_hide_history": "Komponens előzményeinek elrejtése",
|
||||
"cmd.component_insert_design": "Terv beszúrása",
|
||||
"cmd.component_links": "Kapcsolatok listázása",
|
||||
"cmd.component_list": "Komponensek listázása",
|
||||
@@ -875,6 +878,7 @@
|
||||
"cmd.component_rename": "Komponens átnevezése",
|
||||
"cmd.component_replace_with_link": "Csere hivatkozásra",
|
||||
"cmd.component_revert_position": "Pozíció visszaállítása",
|
||||
"cmd.component_show_history": "Komponens előzményeinek megjelenítése",
|
||||
"cmd.component_unlock": "Tartalom feloldása",
|
||||
"cmd.component_update_links": "Kapcsolatok frissítése",
|
||||
"cmd.config_activate": "Konfiguráció aktiválása",
|
||||
@@ -885,6 +889,7 @@
|
||||
"cmd.config_rule": "Konfigurációs szabály",
|
||||
"cmd.config_rules": "Konfigurációs szabályok",
|
||||
"cmd.config_table": "Konfigurációs táblázat megjelenítése",
|
||||
"cmd.construct.plane.axis_point": "Sík tengelyen és ponton át",
|
||||
"cmd.construct_axis_edge": "Tengely élen át",
|
||||
"cmd.construct_axis_normal_to_face": "Lapra merőleges tengely",
|
||||
"cmd.construct_axis_through_cylinder": "Tengely hengeren/kúpon/tóruszon át",
|
||||
@@ -933,6 +938,11 @@
|
||||
"cmd.explode_show": "Robbantott nézet megjelenítése",
|
||||
"cmd.expr_evaluate": "Kifejezés kiértékelése",
|
||||
"cmd.face_delete": "Lap törlése",
|
||||
"cmd.file.export_3mf": "3MF exportálása",
|
||||
"cmd.file.export_obj": "OBJ exportálása",
|
||||
"cmd.file.export_step": "STEP exportálása",
|
||||
"cmd.file.export_stl": "STL exportálása",
|
||||
"cmd.file.export_urdf": "URDF exportálása",
|
||||
"cmd.file_export": "Exportálás",
|
||||
"cmd.file_insert_mesh": "Háló beszúrása",
|
||||
"cmd.file_insert_step": "STEP beszúrása",
|
||||
@@ -950,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "Komponensszínek bekapcsolása",
|
||||
"cmd.inspect_colored_components_disable": "Komponensszínek kikapcsolása",
|
||||
"cmd.inspect_compare": "Testek összehasonlítása",
|
||||
"cmd.inspect_compare_picture": "Kép összehasonlítása",
|
||||
"cmd.inspect_continuity": "Folytonosságelemzés",
|
||||
"cmd.inspect_curvature_comb": "Görbületi fésű elemzése",
|
||||
"cmd.inspect_curvature_map": "Görbülettérkép-elemzés",
|
||||
@@ -1040,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "Pontfelhő szerkesztése",
|
||||
"cmd.pointcloud_insert": "Pontfelhő beszúrása",
|
||||
"cmd.pointcloud_list": "Pontfelhők",
|
||||
"cmd.render.compare.divider": "Renderösszehasonlító elválasztó mozgatása",
|
||||
"cmd.render.result.discard": "Renderelési eredmény elvetése",
|
||||
"cmd.render.result.select": "Renderelési eredmény kiválasztása",
|
||||
"cmd.render_aging_describe": "Rétegzett felületöregedés",
|
||||
"cmd.render_backends": "Renderelő motorok",
|
||||
"cmd.render_batch": "Összes felvétel renderelése",
|
||||
@@ -1254,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "Idővonal-marker mozgatása",
|
||||
"cmd.timeline_stop": "Előzmények leállítása",
|
||||
"cmd.timeline_suppress": "Elem elnyomása",
|
||||
"cmd.ui.assistant": "Asszisztens megjelenítése/elrejtése",
|
||||
"cmd.view.render_browser": "Renderböngésző",
|
||||
"cmd.view.render_dock": "Renderpanel áthelyezése",
|
||||
"cmd.view.render_focus": "Renderfókusz nézet",
|
||||
@@ -1288,9 +1303,11 @@
|
||||
"cmd.view_tool": "Navigációs eszköz",
|
||||
"cmd.view_top": "Felülnézet",
|
||||
"cmd.view_visual_style": "Vizuális stílus",
|
||||
"color.blue": "Kék",
|
||||
"color.copy": "Kattintson a színértékek másolásához",
|
||||
"color.edit": "Kattintson a szín szerkesztéséhez",
|
||||
"color.gamma": "Színértékek megjelenítése 0–255 gamma térben",
|
||||
"color.green": "Zöld",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "Adja meg: #rrggbb vagy #rrggbbaa; hat számjegy esetén az átlátszatlanság megmarad",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1298,6 +1315,7 @@
|
||||
"color.linear": "Színértékek megjelenítése 0–1 lineáris térben",
|
||||
"color.recent": "Legutóbbi színek",
|
||||
"color.recent_help": "A bármelyik színmezőben kiválasztott színek itt jelennek meg",
|
||||
"color.red": "Piros",
|
||||
"color.saturation": "Telítettség (százalék)",
|
||||
"color.saturation_value": "Telítettség és érték",
|
||||
"color.selected": "Kijelölt szín",
|
||||
@@ -1305,6 +1323,8 @@
|
||||
"color.step_up": "Lépés felfelé",
|
||||
"color.value": "Érték, a fényerő (százalék)",
|
||||
"config.column_filter_empty": "Nincs egyező oszlop",
|
||||
"config.rule_parameter_columns": "Csak azok a paraméterek szerepelnek, amelyeknek van oszlopa a táblázatban. Az Oszlop hozzáadása (+) paranccsal vehet fel egyet, hogy itt használhassa.",
|
||||
"config.rule_suppression_columns": "Csak azok az elemek szerepelnek, amelyeknek van Elnyomás oszlopa a táblázatban. Az Oszlop hozzáadása (+) paranccsal vehet fel egyet, hogy innen vezérelje.",
|
||||
"contact.components_count": "{count} komponens",
|
||||
"contact.enabled_label": "engedélyezve",
|
||||
"coupling.gear_1": "1. fogaskerék",
|
||||
@@ -1353,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "Válasszon testeket vagy elemeket: a vázlat egyik sem. Ismételje a görbéit a vázlat saját Minta vagy Tükrözés parancsával, vagy húzza ki előbb.",
|
||||
"dialog.select": "↖ Kijelölés",
|
||||
"dialog.selected_count": "↖ {count} kijelölve",
|
||||
"dialog.selection_constraint": "Kényszer {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · görbe {id}",
|
||||
"dialog.selection_guidance": "Válasszon ki egy bemenetet a módosításhoz. A kijelölt objektumokra kattintva eltávolíthatja őket; a Ctrl/Cmd-kattintás is váltja a kijelölést. Tekintse át az előnézetet, majd nyomja meg az OK gombot.",
|
||||
"dialog.selection_midpoint": "Felezőpont · {id}",
|
||||
"dialog.selection_profile": "{sketch} · profil {number}",
|
||||
"dialog.selection_step": "{step}/{total}: {input} kijelölése",
|
||||
"dialog.step": "Lépés",
|
||||
"document.changed_on_disk": "A(z) {name} megváltozott a lemezen",
|
||||
@@ -1436,6 +1460,7 @@
|
||||
"help.construct.axis.two_points": "Szerkesztőtengelyt hoz létre két ponton, csúcson vagy vázlatponton át. Használja két ismert helyzet között húzódó tengelyhez.",
|
||||
"help.construct.plane.along_path": "Szerkesztősíkot hoz létre egy élre vagy görbére merőlegesen, annak egy adott helyén. Használja söpréshez való profil vagy egy útvonal keresztmetszetének megrajzolására.",
|
||||
"help.construct.plane.at_angle": "Szerkesztősíkot hoz létre, amely egy síktól vagy laptól egy tengely vagy él körül adott szöggel van elforgatva. Használja ferde vázlatokhoz, például szögben álló konzolhoz.",
|
||||
"help.construct.plane.axis_point": "Szerkesztősíkot hoz létre, amely tartalmaz egy tengelyt és egy, a tengelyen kívül eső pontot. Válasszon egy origótengelyt vagy szerkesztőtengelyt, egy egyenes élt vagy vázlatvonalat, majd egy csúcsot, vázlatpontot vagy szerkesztőpontot. A tengelyen lévő pont nem határoz meg egyértelmű síkot.",
|
||||
"help.construct.plane.midplane": "Szerkesztősíkot hoz létre két sík vagy sík lap között félúton, vagy, ha szögben találkoznak, felezve őket. Használja tükrözés szimmetriasíkjaként.",
|
||||
"help.construct.plane.offset": "Szerkesztősíkot hoz létre egy síkkal vagy sík lappal párhuzamosan, adott távolságra vagy egy ponton át. Használja, hogy egy lap fölött meghatározott magasságban rajzoljon vázlatot.",
|
||||
"help.construct.plane.perpendicular": "Szerkesztősíkot hoz létre, amely tartalmaz egy egyenest vagy élt, és merőleges egy síkra vagy lapra. Használja, hogy egy modellen egy él mentén függőlegesen álló vázlatot rajzoljon.",
|
||||
@@ -1812,6 +1837,8 @@
|
||||
"joint_move.blocked_hint": "Ellenőrizze a csukló típusát (például Merev), a földhöz rögzítést, a csuklókorlátokat és a paraméterrel vezérelt értékeket.",
|
||||
"joint_move.blocked_title": "Ez az alkatrész nem mozgatható",
|
||||
"joint_move.coarse": "Forgatási lépés a tengely közelében (°)",
|
||||
"joint_move.conflict": "Ennek az alkatrésznek egy csuklója ütközik a többivel: {joint}.",
|
||||
"joint_move.conflict_hint": "Szerkessze, nyomja el vagy törölje az ütköző csuklót, hogy a többi csukló elhelyezhesse az alkatrészt.",
|
||||
"joint_move.distance": "Távolság a kezdőponttól",
|
||||
"joint_move.done": "Kész",
|
||||
"joint_move.fine": "Forgatási lépés a tengelytől távol (°)",
|
||||
@@ -1911,6 +1938,8 @@
|
||||
"motion.track_name": "{number}. sáv",
|
||||
"nav.alias.help": "Shift+Alt+bal húzás: keringés, Shift+Alt+középső húzás: pásztázás, Shift+Alt+jobb húzás: nagyítás; a középső húzás is pásztáz, a jobb húzás is keringést végez",
|
||||
"nav.blender.help": "Középső húzás: keringés, Shift+középső húzás: pásztázás, Ctrl+középső húzás: nagyítás; a jobb húzás nem mozgatja a kamerát",
|
||||
"nav.creo.help": "Középső húzás: keringés, Shift+középső húzás: pásztázás, Ctrl+középső függőleges húzás: nagyítás (lefelé húzva közelít); az egérgörgő görgetése: nagyítás",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "Az egér és a 3D-egér mindkét póluson átforgat, és megőrzi az elfordulást. A Shiftre történő tengelyhez illesztés ki van kapcsolva.",
|
||||
"nav.free_orbit.label": "Szabad keringés",
|
||||
"nav.free_orbit.snap_help": "Az egér és a 3D-egér mindkét póluson átforgat, és megőrzi az elfordulást. Keringéskor tartsa lenyomva a Shiftet a nézetirány és az elfordulás illesztéséhez.",
|
||||
@@ -2063,12 +2092,19 @@
|
||||
"parts.family.button_head_cap_screw": "Belső kulcsnyílású lencsefejű csavar",
|
||||
"parts.family.dowel_pin": "Hengeres illesztőcsap",
|
||||
"parts.family.hex_bolt": "Hatlapfejű csavar",
|
||||
"parts.family.hex_cap_screw_inch": "Hatlapfejű csavar (hüvelykes)",
|
||||
"parts.family.hex_nut": "Hatlapú anya",
|
||||
"parts.family.hex_nut_inch": "Hatlapú anya (hüvelykes)",
|
||||
"parts.family.set_screw": "Belső kulcsnyílású szárnélküli csavar, lapos végű",
|
||||
"parts.family.socket_head_cap_screw": "Belső kulcsnyílású hengeresfejű csavar",
|
||||
"parts.family.washer": "Sima alátét",
|
||||
"parts.family.washer_sae": "Sima alátét, SAE (keskeny)",
|
||||
"parts.family.washer_uss": "Sima alátét, USS (széles)",
|
||||
"parts.holes": "Furatok (opcionális)",
|
||||
"parts.placement_hint": "jelöljön ki egy vagy több furatot, hogy mindegyikbe beillesszen egy alkatrészt, vagy az OK az origóba helyezi",
|
||||
"parts.system": "Mértékegység-rendszer",
|
||||
"parts.system.inch": "Hüvelyk",
|
||||
"parts.system.metric": "Metrikus",
|
||||
"pattern.alignment_2": "2. igazítás",
|
||||
"pattern.alignment_hint": "A darabszám tartalmazza az eredetit. A Közép kiegyensúlyozza a másolatokat; a Megfordítás páros összegnél kijelöli a többletoldalt.",
|
||||
"pattern.direction_1": "1. irány",
|
||||
@@ -2130,6 +2166,8 @@
|
||||
"prefs.button_left": "Húzás bal gombbal",
|
||||
"prefs.button_middle": "Húzás középső gombbal",
|
||||
"prefs.button_right": "Húzás jobb gombbal",
|
||||
"prefs.carousel_next": "Következő",
|
||||
"prefs.carousel_previous": "Előző",
|
||||
"prefs.choose_font": "Betűtípusfájl kiválasztása…",
|
||||
"prefs.click": "Kattintás",
|
||||
"prefs.contact_shading": "Lágy érintkezési árnyalás",
|
||||
@@ -2142,6 +2180,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Görbült felületek",
|
||||
"prefs.curves": "Görbült felületek",
|
||||
"prefs.cut_preview_tint": "Az eltávolított anyag megjelenítése pirossal",
|
||||
"prefs.cut_preview_tint_help": "Az előnézet az elem által eltávolított anyagot és a lecserélt alakzatot piros, áttetsző szellemképpel tölti ki. Kikapcsolva ezek a kitöltések teljesen átlátszóak, és csak a körvonalaik láthatók.",
|
||||
"prefs.dark_parts_shading": "A sötét alkatrészek formájának megőrzése",
|
||||
"prefs.dark_parts_shading_help": "A nézet árnyalása lineáris fényben, lágy égbolt-kitöltéssel, hogy a sötét lapokon megmaradjon az átmenet, és ne váljanak feketévé",
|
||||
"prefs.dead_zone_note": "A sraffozott sáv a holtsáv: az azon belüli mozgást figyelmen kívül hagyja.",
|
||||
@@ -2200,6 +2240,8 @@
|
||||
"prefs.interface_language": "Felület nyelve",
|
||||
"prefs.interface_note": "A SolidCraft megjelenése és elemeinek elhelyezése.",
|
||||
"prefs.interval_minutes": "{n} perc",
|
||||
"prefs.invert_curved_roll_arrows": "Az íves gördülőnyilak megfordítása",
|
||||
"prefs.invert_curved_roll_arrows_note": "A nézetkocka sarkában lévő két íves nyíl a nézetet a képernyő síkjában gördíti. Be: az óramutató járásával megegyező nyíl ellentétes irányba gördít, az ellentétes nyíl pedig az óramutató járásával megegyező irányba. Ki: minden nyíl abba az irányba gördíti a nézetet, amerre ível.",
|
||||
"prefs.invert_navigator_arrows": "A nézetkocka forgatónyilainak megfordítása",
|
||||
"prefs.invert_navigator_arrows_note": "A kocka körüli nyilak úgy keringetik a kamerát, ahogy mutatnak, mint a Fusion és az Inventor esetében: a bal oldali nyíl jobbra mutat, és a kamerát jobbra lendíti. Ki: a nyíl a kockát afelé az oldal felé forgatja, amelyiken van, így a bal nyíl a kamerát balra lendíti. Az íves gördülőnyilakat ez nem érinti.",
|
||||
"prefs.keep_work_on_tab_switch": "A párbeszédablakok és eszközök nyitva tartása tervek közötti váltáskor",
|
||||
@@ -2259,6 +2301,8 @@
|
||||
"prefs.placement": "Elhelyezés",
|
||||
"prefs.prefer_free_sketch_points": "Átfedő pontoknál a mozgatható vázlatpontok előnyben részesítése",
|
||||
"prefs.preview_choices": "A választások előnézete",
|
||||
"prefs.preview_tint_new_faces": "Az új felület színezése az előnézetben",
|
||||
"prefs.preview_tint_new_faces_help": "Egy testet módosító elem előnézete az általa hozzáadott felületet az előnézeti színnel rajzolhatja ki. Kikapcsolva a test önmagaként jelenik meg.",
|
||||
"prefs.print_face_highlight": "Nyomtatási lap kiemelése",
|
||||
"prefs.print_face_highlight_tip": "Milyen erősen színezi és körvonalazza a nézet a test nyomtatási lapját. 0 % esetén nincs kiemelés.",
|
||||
"prefs.profile_active": "Aktív profil",
|
||||
@@ -2556,6 +2600,10 @@
|
||||
"render.base_color": "Alapszín · sRGB",
|
||||
"render.base_metallic": "Fémes hányad",
|
||||
"render.base_opaque_only": "Ez a szerkesztő megőrzi az átlátszatlan test szemantikáját. Az optikai áteresztéshez használja a külön üvegvezérlőket.",
|
||||
"render.batch_all_shots": "Összes felvétel ({count})",
|
||||
"render.batch_empty": "Mentsen el egy felvételt, mielőtt az összes felvételt renderelné.",
|
||||
"render.batch_limit": "Az összes felvétel renderelése legfeljebb 32 felvételre korlátozott.",
|
||||
"render.batch_status": "{running} fut · {waiting} vár",
|
||||
"render.bloom_enable": "Bloom megjelenítése",
|
||||
"render.bloom_help": "Kreatív, csak a kimenetre vonatkozó fénykisugárzás a pozitív lineáris csúcsfényekből, három normalizált dobozszűrő-menettel szétterítve. A sugár kimeneti pixelben értendő. Lineáris megjelenítési fényhez adódik az sRGB előtt; nem lencsehatás, nem térfogati effektus és nem javítja a konvergenciát. A nyers EXR változatlan.",
|
||||
"render.bloom_opaque": "A bloomhoz átlátszatlan kimenet szükséges. Az átlátszó PNG-t a program elutasítja, nem lapítja csendben.",
|
||||
@@ -2666,6 +2714,13 @@
|
||||
"render.choose_look": "Először válasszon stílust a Megjelenések között",
|
||||
"render.chromatic_offset": "Kromatikus éleltolás",
|
||||
"render.clear_stage": "Mentett díszlet eltávolítása",
|
||||
"render.compare_divider": "Összehasonlító elválasztó",
|
||||
"render.compare_live": "Élő",
|
||||
"render.compare_live_preview": "Élő előnézet",
|
||||
"render.compare_render": "{number}. render",
|
||||
"render.compare_side": "{number}. render · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} s · {renderer} · rögzítve, revízió: {revision}",
|
||||
"render.compare_swap": "Csere",
|
||||
"render.copy_request": "JSON másolása",
|
||||
"render.cpu_aa_help": "Térbeli minták: a több minta javítja az éleket, és növeli a renderelési időt.",
|
||||
"render.cpu_area_add": "Téglalap alakú CPU-fényforrás hozzáadása",
|
||||
@@ -3415,6 +3470,10 @@
|
||||
"render.result_retained": "Az utolsó kész kép látható; újabb renderelés még nem váltotta le.",
|
||||
"render.result_sequence": "Ellenőrzőpont",
|
||||
"render.result_title": "Renderelési eredmény",
|
||||
"render.results_choose": "Megtartott eredmények",
|
||||
"render.results_label": "{number}. render · {shot} · {seconds} s",
|
||||
"render.results_stale_label": "{result} [elavult]",
|
||||
"render.results_unnamed": "Kép",
|
||||
"render.return_design": "Vissza a Tervezéshez",
|
||||
"render.review_stale": "Ez az áttekintés elavult. Az alkalmazás előtt oldja fel, vagy ellenőrizze újra.",
|
||||
"render.ribbon_aging": "Öregítés",
|
||||
@@ -3898,10 +3957,7 @@
|
||||
"the two origins are on the same component": "a két origó ugyanazon a komponensen van",
|
||||
"thread.automatic_size": "Automatikus méret",
|
||||
"thread.designation": "Jelölés",
|
||||
"thread.direction": "Menet iránya",
|
||||
"thread.iso_metric": "ISO metrikus",
|
||||
"thread.left_hand": "Balmenetes",
|
||||
"thread.right_hand": "Jobbmenetes",
|
||||
"thread.select_size": "Méret kiválasztása",
|
||||
"timeline.delete_after_marker_one": "1 elem törlése az előzményjelölő után",
|
||||
"timeline.delete_after_marker_other": "{count} elem törlése az előzményjelölő után",
|
||||
@@ -4075,6 +4131,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Futtassa ezt egy terminálban, majd nyomja meg újra a Csatlakozás gombot:",
|
||||
"ui.agent_chat_auth_title": "Bejelentkezés az ügynökbe",
|
||||
"ui.agent_chat_cancel": "Mégse",
|
||||
"ui.agent_chat_cancel_timeout": "Az ügynök nem válaszolt a Leállításra, ezért le lett választva. A folytatáshoz csatlakozzon újra.",
|
||||
"ui.agent_chat_cannot_start": "Telepítve van, de nem indul el: {error}",
|
||||
"ui.agent_chat_changes": "Módosítások: {n}, egy visszavonási lépés",
|
||||
"ui.agent_chat_changes_n": "{n} módosítás",
|
||||
@@ -4084,11 +4141,13 @@
|
||||
"ui.agent_chat_command": "Ügynök parancsa",
|
||||
"ui.agent_chat_connect": "Csatlakozás",
|
||||
"ui.agent_chat_copy": "Másolás",
|
||||
"ui.agent_chat_copy_message": "Üzenet másolása",
|
||||
"ui.agent_chat_ctx_bodies": "Testek",
|
||||
"ui.agent_chat_ctx_elements": "Geometriai elemek",
|
||||
"ui.agent_chat_ctx_features": "Elemek",
|
||||
"ui.agent_chat_ctx_more": "+{n} további",
|
||||
"ui.agent_chat_ctx_other": "Egyéb",
|
||||
"ui.agent_chat_custom_heading": "Egyéni modell",
|
||||
"ui.agent_chat_custom_hint": "Bármely program, amely az Agent Client Protocolt használja stdin-en és stdout-on. Shell nem futtatja.",
|
||||
"ui.agent_chat_denied": "Elutasítva",
|
||||
"ui.agent_chat_deny": "Elutasítás",
|
||||
@@ -4132,8 +4191,16 @@
|
||||
"ui.agent_chat_mode_all": "Minden",
|
||||
"ui.agent_chat_mode_ask": "Kérdez",
|
||||
"ui.agent_chat_mode_edits": "Szerkesztés",
|
||||
"ui.agent_chat_model_custom": "Egyéni használata: {model}",
|
||||
"ui.agent_chat_model_no_tools": "A(z) {model} nem tudja használni a SolidCraft eszközeit, ezért csak csevegni lehet vele. Válasszon eszköztámogatással rendelkező modellt.",
|
||||
"ui.agent_chat_model_rejected": "Az ügynök nem fogadta el a(z) {model} modellt: {error}",
|
||||
"ui.agent_chat_model_search": "Modellek keresése…",
|
||||
"ui.agent_chat_model_tip": "Az ügynök által használt modell. A választás megmarad ennél az ügynöknél.",
|
||||
"ui.agent_chat_models_failed": "A modellek nem listázhatók: {error}",
|
||||
"ui.agent_chat_models_heading": "Elérhető modellek",
|
||||
"ui.agent_chat_models_listing": "Modellek listázása…",
|
||||
"ui.agent_chat_models_none": "A végpont nem listáz modelleket",
|
||||
"ui.agent_chat_models_not_a_list": "A válasz nem modellek listája volt",
|
||||
"ui.agent_chat_new": "Új csevegés",
|
||||
"ui.agent_chat_new_confirm": "Új csevegést indít? A jelenlegi törlődik.",
|
||||
"ui.agent_chat_next_picture": "Következő kép",
|
||||
@@ -4407,14 +4474,21 @@
|
||||
"ui.construction.contact_hint": "Érintő: válasszon egy kört/ívet vagy nyitott spline-t/kúpgörbét, majd az érintési pontot (szabadformájú görbék: végpontok)",
|
||||
"ui.construction.external_hint": "Érintő: válasszon egy kört vagy ívet, egy külső pontot, majd a kívánt megoldást",
|
||||
"ui.construction.free_hint": "Szerkesztőegyenes: kattintson két pontra; jobb kattintással újrakezdheti",
|
||||
"ui.construction.normal_needs_length": "A végtelen egyenes mindig szerkesztőegyenes: normál vonal rajzolásához adjon meg hosszt",
|
||||
"ui.construction.point_hint": "Szerkesztőegyenes: kattintson arra a pontra, amelyen át kell haladnia",
|
||||
"ui.construction.reference_hint": "Szerkesztőegyenes: válasszon egy egyenest, majd egy pontot; jobb kattintással módosíthatja a referenciát",
|
||||
"ui.construction.ribbon_label": "Segédvonalak",
|
||||
"ui.construction.side_hint": "Szerkesztőegyenes: kattintson arra az oldalra, amerre a vonal halad; jobb kattintással újrakezdheti",
|
||||
"ui.context_menu": "Helyi menü",
|
||||
"ui.continuity_crease": "Törés",
|
||||
"ui.continuity_curvature": "Görbület",
|
||||
"ui.continuity_curvature_change": "Görbületváltozás",
|
||||
"ui.continuity_g0": "G0 törés",
|
||||
"ui.continuity_g1": "G1 érintő",
|
||||
"ui.continuity_g2": "G2 görbület",
|
||||
"ui.continuity_gap": "Rés",
|
||||
"ui.continuity_position": "Pozíció",
|
||||
"ui.continuity_tangent": "Érintő",
|
||||
"ui.copy": "Másolás",
|
||||
"ui.corner": "Sarok",
|
||||
"ui.corners": "Sarkok",
|
||||
@@ -4675,6 +4749,7 @@
|
||||
"ui.hideOrigin": "Origó elrejtése",
|
||||
"ui.hidePlanes": "Síkok elrejtése",
|
||||
"ui.hideProfile": "Profil elrejtése",
|
||||
"ui.hide_component_history": "Előzményeinek elrejtése",
|
||||
"ui.hide_origin_by_default": "Origó elrejtése alapértelmezetten",
|
||||
"ui.hide_origin_note": "Az új tervek az Origó mappa elrejtésével indulnak. A terv megtartja a saját Origó-beállítását, amint megjeleníti vagy elrejti.",
|
||||
"ui.history_back": "Egy lépéssel vissza",
|
||||
@@ -4682,6 +4757,7 @@
|
||||
"ui.history_edge_hint": "kattintson duplán az élre",
|
||||
"ui.history_first": "Ugrás az elejére",
|
||||
"ui.history_forward": "Egy lépéssel előre",
|
||||
"ui.history_hide_hidden": "Rejtett komponensek elrejtése",
|
||||
"ui.history_hide_names": "Nevek elrejtése",
|
||||
"ui.history_last": "Ugrás a végére",
|
||||
"ui.history_marker_tip": "Előzményjelölő: húzással visszagörgetheti a modellt; az új elemek ide kerülnek",
|
||||
@@ -4691,10 +4767,12 @@
|
||||
"ui.history_play_button_show": "Lejátszás gomb megjelenítése",
|
||||
"ui.history_play_finish_action": "Az Előzmények lejátszása előtt fejezze be az aktuális műveletet",
|
||||
"ui.history_play_roll_back": "Lejátszás előtt mozgassa vissza az előzménymarkert",
|
||||
"ui.history_show_hidden": "Rejtett komponensek megjelenítése",
|
||||
"ui.history_show_names": "Nevek megjelenítése",
|
||||
"ui.history_sketch_listed_under": "Itt szerepel: {feature}",
|
||||
"ui.history_title": "Előzmények",
|
||||
"ui.history_toggle_names": "Elemnevek megjelenítése vagy elrejtése",
|
||||
"ui.history_waits_for_editor": "A Visszavonás, a Mégis és az Előzmények addig várnak, amíg az elemszerkesztő meg nem nyílik",
|
||||
"ui.hole": "Furat",
|
||||
"ui.hole_tap_type": "Furat menettípusa",
|
||||
"ui.hole_type": "Furat típusa",
|
||||
@@ -4710,6 +4788,7 @@
|
||||
"ui.insert_design.saved_file": "A(z) {name} nem mentett módosításokat tartalmaz, ezért a mentett fájlja lett beszúrva. Mentse el, majd a Kapcsolatok frissítése paranccsal hozza át a módosításokat.",
|
||||
"ui.insert_design.unsaved": "Nem mentett módosítások",
|
||||
"ui.inspect": "Vizsgálat",
|
||||
"ui.instances": "Példányok",
|
||||
"ui.intersect": "Metszet",
|
||||
"ui.isolate": "Elkülönítés",
|
||||
"ui.join": "Egyesítés",
|
||||
@@ -4974,6 +5053,7 @@
|
||||
"ui.showDimension": "Méret megjelenítése",
|
||||
"ui.showOrigin": "Origó megjelenítése",
|
||||
"ui.showPlanes": "Síkok megjelenítése",
|
||||
"ui.show_component_history": "Előzményeinek megjelenítése",
|
||||
"ui.show_constraints": "Kényszerek",
|
||||
"ui.show_construction_geometries": "Szerkesztő geometriák",
|
||||
"ui.show_dimensions": "Méretek",
|
||||
@@ -5156,7 +5236,7 @@
|
||||
"update.check_unavailable": "Ehhez a telepítéshez nem érhető el a frissítések keresése.",
|
||||
"update.checking": "Keresés…",
|
||||
"update.copy": "Másolás",
|
||||
"update.dmg_opened": "A lemezkép meg van nyitva és ellenőrizve: húzza a SolidCraftot az Alkalmazások mappába, lecserélve a régi példányt, majd indítsa újra a SolidCraftot.",
|
||||
"update.dmg_opened": "A lemezkép meg van nyitva és ellenőrizve: lépjen ki a SolidCraftból, húzza az új SolidCraftot az Alkalmazások mappába, lecserélve a régi példányt, majd nyissa meg újra a SolidCraftot.",
|
||||
"update.download": "Letöltés",
|
||||
"update.download_open": "Letöltés és megnyitás",
|
||||
"update.downloading": "Letöltés és ellenőrzés… {done}/{total} MB",
|
||||
@@ -5200,6 +5280,7 @@
|
||||
"web.open_link": "Megnyitja a csatolt tervet?",
|
||||
"web.open_link_confirm": "Mentetlen módosítások vannak. Megnyitja a csatolt tervet új fülön?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ választás · Enter / Tab kiegészítés · Esc elvetés",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ választás · Enter alkalmazza ezt az értéket",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ választás · Enter alkalmazza a beírtakat · Esc elvetés",
|
||||
"⌘Enter continues on GitHub.": "A ⌘Enter a GitHubon folytatja."
|
||||
}
|
||||
|
||||
+85
-4
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "Persegi panjang",
|
||||
"Rectangular Tube": "Pipa Persegi Panjang",
|
||||
"Reference face": "Muka referensi",
|
||||
"Reference plane": "Bidang referensi",
|
||||
"Relief Depth": "Kedalaman Relief",
|
||||
"Relief Width": "Lebar Relief",
|
||||
"Remove Tools": "Hapus Alat",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "Penampang",
|
||||
"Section (optional)": "Penampang (opsional)",
|
||||
"Section position": "Posisi penampang",
|
||||
"Sections": "Penampang",
|
||||
"Sections (one sketch each)": "Penampang (satu sketsa per penampang)",
|
||||
"Select Other": "Pilih Lainnya",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Select Other: tahan tombol kiri diam di model (atau klik kanan › Select Other…) untuk mendaftar semua yang ada di bawah kursor; klik lagi di titik yang sama untuk memilih benda berikutnya. Klik dua kali tombol tengah untuk ke Home (Preferensi › Navigasi).",
|
||||
@@ -859,6 +861,7 @@
|
||||
"cmd.component_extract": "Ekstrak ke File",
|
||||
"cmd.component_finish_edit": "Selesai Edit",
|
||||
"cmd.component_from_bodies": "Buat Komponen dari Bodi",
|
||||
"cmd.component_hide_history": "Sembunyikan Riwayat Komponen",
|
||||
"cmd.component_insert_design": "Sisipkan Desain",
|
||||
"cmd.component_links": "Daftar Tautan",
|
||||
"cmd.component_list": "Daftar Komponen",
|
||||
@@ -875,6 +878,7 @@
|
||||
"cmd.component_rename": "Ganti Nama Komponen",
|
||||
"cmd.component_replace_with_link": "Ganti dengan Tautan",
|
||||
"cmd.component_revert_position": "Kembalikan Posisi",
|
||||
"cmd.component_show_history": "Tampilkan Riwayat Komponen",
|
||||
"cmd.component_unlock": "Buka Kunci Isi",
|
||||
"cmd.component_update_links": "Perbarui Tautan",
|
||||
"cmd.config_activate": "Aktifkan Konfigurasi",
|
||||
@@ -885,6 +889,7 @@
|
||||
"cmd.config_rule": "Aturan Konfigurasi",
|
||||
"cmd.config_rules": "Aturan Konfigurasi",
|
||||
"cmd.config_table": "Tampilkan Tabel Konfigurasi",
|
||||
"cmd.construct.plane.axis_point": "Bidang Melalui Sumbu dan Titik",
|
||||
"cmd.construct_axis_edge": "Sumbu Melalui Tepi",
|
||||
"cmd.construct_axis_normal_to_face": "Sumbu Tegak Lurus Muka",
|
||||
"cmd.construct_axis_through_cylinder": "Sumbu Melalui Silinder/Kerucut/Torus",
|
||||
@@ -933,6 +938,11 @@
|
||||
"cmd.explode_show": "Tampilkan Tampilan Urai",
|
||||
"cmd.expr_evaluate": "Evaluasi Ekspresi",
|
||||
"cmd.face_delete": "Hapus Muka",
|
||||
"cmd.file.export_3mf": "Ekspor 3MF",
|
||||
"cmd.file.export_obj": "Ekspor OBJ",
|
||||
"cmd.file.export_step": "Ekspor STEP",
|
||||
"cmd.file.export_stl": "Ekspor STL",
|
||||
"cmd.file.export_urdf": "Ekspor URDF",
|
||||
"cmd.file_export": "Ekspor",
|
||||
"cmd.file_insert_mesh": "Sisipkan Mesh",
|
||||
"cmd.file_insert_step": "Sisipkan STEP",
|
||||
@@ -950,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "Aktifkan Warna Komponen",
|
||||
"cmd.inspect_colored_components_disable": "Nonaktifkan Warna Komponen",
|
||||
"cmd.inspect_compare": "Bandingkan Bodi",
|
||||
"cmd.inspect_compare_picture": "Bandingkan dengan Gambar",
|
||||
"cmd.inspect_continuity": "Analisis Kontinuitas",
|
||||
"cmd.inspect_curvature_comb": "Analisis Sisir Kelengkungan",
|
||||
"cmd.inspect_curvature_map": "Analisis Peta Kelengkungan",
|
||||
@@ -1040,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "Edit Awan Titik",
|
||||
"cmd.pointcloud_insert": "Sisipkan Awan Titik",
|
||||
"cmd.pointcloud_list": "Awan Titik",
|
||||
"cmd.render.compare.divider": "Pindahkan Pembagi Perbandingan Render",
|
||||
"cmd.render.result.discard": "Buang Hasil Render",
|
||||
"cmd.render.result.select": "Pilih Hasil Render",
|
||||
"cmd.render_aging_describe": "Penuaan Permukaan Berlapis",
|
||||
"cmd.render_backends": "Backend Render",
|
||||
"cmd.render_batch": "Render Semua Bidikan",
|
||||
@@ -1254,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "Pindahkan Penanda Linimasa",
|
||||
"cmd.timeline_stop": "Hentikan Riwayat",
|
||||
"cmd.timeline_suppress": "Tekan Fitur",
|
||||
"cmd.ui.assistant": "Alihkan Asisten",
|
||||
"cmd.view.render_browser": "Penjelajah Render",
|
||||
"cmd.view.render_dock": "Pindah Dok Render",
|
||||
"cmd.view.render_focus": "Tampilan Fokus Render",
|
||||
@@ -1288,9 +1303,11 @@
|
||||
"cmd.view_tool": "Alat navigasi",
|
||||
"cmd.view_top": "Tampilan Atas",
|
||||
"cmd.view_visual_style": "Gaya visual",
|
||||
"color.blue": "Biru",
|
||||
"color.copy": "Klik untuk menyalin nilai warna",
|
||||
"color.edit": "Klik untuk mengedit warna",
|
||||
"color.gamma": "Menampilkan nilai warna dalam ruang gamma 0-255",
|
||||
"color.green": "Hijau",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "Masukkan #rrggbb atau #rrggbbaa; enam digit mempertahankan opasitas",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1298,6 +1315,7 @@
|
||||
"color.linear": "Menampilkan nilai warna dalam ruang linear 0-1",
|
||||
"color.recent": "Warna terbaru",
|
||||
"color.recent_help": "Warna yang Anda pilih di kolom warna mana pun muncul di sini",
|
||||
"color.red": "Merah",
|
||||
"color.saturation": "Saturasi (persen)",
|
||||
"color.saturation_value": "Saturasi dan nilai",
|
||||
"color.selected": "Warna terpilih",
|
||||
@@ -1305,6 +1323,8 @@
|
||||
"color.step_up": "Naikkan",
|
||||
"color.value": "Nilai, yaitu kecerahan (persen)",
|
||||
"config.column_filter_empty": "Tidak ada kolom yang cocok",
|
||||
"config.rule_parameter_columns": "Hanya parameter yang memiliki kolom di tabel yang dicantumkan. Tambahkan satu dengan Tambah Kolom (+) untuk memakainya di sini.",
|
||||
"config.rule_suppression_columns": "Hanya fitur yang memiliki kolom Tekan di tabel yang dicantumkan. Tambahkan satu dengan Tambah Kolom (+) untuk mengendalikannya di sini.",
|
||||
"contact.components_count": "{count} komponen",
|
||||
"contact.enabled_label": "aktif",
|
||||
"coupling.gear_1": "Roda Gigi 1",
|
||||
@@ -1353,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "Pilih bodi atau fitur: sketsa bukan keduanya. Ulangi kurvanya dengan Pola atau Cermin milik sketsa itu sendiri, atau ekstrusi dulu.",
|
||||
"dialog.select": "↖ Pilih",
|
||||
"dialog.selected_count": "↖ {count} terpilih",
|
||||
"dialog.selection_constraint": "Batasan {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · kurva {id}",
|
||||
"dialog.selection_guidance": "Pilih input untuk mengubahnya. Klik objek terpilih untuk menghapusnya; Ctrl/Cmd-klik juga mengalihkannya. Tinjau pratinjau, lalu tekan OK.",
|
||||
"dialog.selection_midpoint": "Titik tengah · {id}",
|
||||
"dialog.selection_profile": "{sketch} · profil {number}",
|
||||
"dialog.selection_step": "{step} dari {total}: Pilih {input}",
|
||||
"dialog.step": "Langkah",
|
||||
"document.changed_on_disk": "{name} berubah di disk",
|
||||
@@ -1436,6 +1460,7 @@
|
||||
"help.construct.axis.two_points": "Membuat sumbu konstruksi melalui dua titik, titik sudut, atau titik sketsa. Gunakan untuk sumbu yang membentang di antara dua posisi yang diketahui.",
|
||||
"help.construct.plane.along_path": "Membuat bidang konstruksi tegak lurus terhadap sebuah tepi atau kurva, pada suatu posisi di sepanjangnya. Gunakan untuk menggambar sketsa profil bagi sweep atau penampang sebuah jalur.",
|
||||
"help.construct.plane.at_angle": "Membuat bidang konstruksi yang diputar dengan suatu sudut dari sebuah bidang atau muka, terhadap sebuah sumbu atau tepi. Gunakan untuk sketsa miring, seperti braket bersudut.",
|
||||
"help.construct.plane.axis_point": "Membuat bidang konstruksi yang memuat sebuah sumbu dan sebuah titik di luar sumbu itu. Pilih sebuah origin atau sumbu konstruksi, sebuah tepi lurus atau garis sketsa, lalu sebuah titik sudut, titik sketsa, atau titik konstruksi. Titik yang berada pada sumbu tidak dapat menentukan satu bidang yang unik.",
|
||||
"help.construct.plane.midplane": "Membuat bidang konstruksi tepat di tengah antara dua bidang atau muka datar, atau membagi dua keduanya di tempat mereka bertemu pada suatu sudut. Gunakan sebagai bidang simetri untuk pencerminan.",
|
||||
"help.construct.plane.offset": "Membuat bidang konstruksi yang sejajar dengan sebuah bidang atau muka datar, pada suatu jarak atau melalui sebuah titik. Gunakan untuk menggambar sketsa pada ketinggian tertentu di atas sebuah muka.",
|
||||
"help.construct.plane.perpendicular": "Membuat bidang konstruksi yang memuat sebuah garis atau tepi dan berdiri tegak lurus terhadap sebuah bidang atau muka. Gunakan untuk menggambar sketsa tegak pada model sepanjang sebuah tepi.",
|
||||
@@ -1812,6 +1837,8 @@
|
||||
"joint_move.blocked_hint": "Periksa tipe sambungan (misalnya Kaku), status ground, batas sambungan, dan nilai yang digerakkan parameter.",
|
||||
"joint_move.blocked_title": "Tidak dapat memindahkan part ini",
|
||||
"joint_move.coarse": "Langkah rotasi dekat sumbu (°)",
|
||||
"joint_move.conflict": "Sebuah sambungan part ini berkonflik dengan yang lain: {joint}.",
|
||||
"joint_move.conflict_hint": "Edit, tekan, atau hapus sambungan yang berkonflik agar sambungan lain dapat menempatkan part.",
|
||||
"joint_move.distance": "Jarak dari awal",
|
||||
"joint_move.done": "Selesai",
|
||||
"joint_move.fine": "Langkah rotasi jauh dari sumbu (°)",
|
||||
@@ -1911,6 +1938,8 @@
|
||||
"motion.track_name": "Track {number}",
|
||||
"nav.alias.help": "Shift+Alt+seret kiri memutar orbit, Shift+Alt+tombol tengah menggeser, Shift+Alt+klik kanan memperbesar/memperkecil; seret tombol tengah juga menggeser dan seret kanan memutar orbit",
|
||||
"nav.blender.help": "Seret tombol tengah memutar orbit, Shift+tombol tengah menggeser, Ctrl+tombol tengah memperbesar/memperkecil; seret kanan tidak menggerakkan kamera",
|
||||
"nav.creo.help": "Seret tombol tengah memutar orbit, Shift+tombol tengah menggeser, Ctrl+seret tombol tengah secara vertikal memperbesar/memperkecil (ke bawah untuk memperbesar); gulir roda untuk zoom",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "Mouse dan mouse 3D berputar melewati kedua kutub dan mempertahankan roll. Penjepretan sumbu dengan Shift dinonaktifkan.",
|
||||
"nav.free_orbit.label": "Orbit bebas",
|
||||
"nav.free_orbit.snap_help": "Mouse dan mouse 3D berputar melewati kedua kutub dan mempertahankan roll. Tahan Shift saat menyeret orbit untuk menjepret arah tampilan dan roll.",
|
||||
@@ -2063,12 +2092,19 @@
|
||||
"parts.family.button_head_cap_screw": "Sekrup kepala bulat (button head)",
|
||||
"parts.family.dowel_pin": "Pin paralel (dowel)",
|
||||
"parts.family.hex_bolt": "Baut / sekrup kepala segi enam",
|
||||
"parts.family.hex_cap_screw_inch": "Sekrup kepala segi enam (cap screw, inci)",
|
||||
"parts.family.hex_nut": "Mur segi enam",
|
||||
"parts.family.hex_nut_inch": "Mur segi enam (inci)",
|
||||
"parts.family.set_screw": "Sekrup set soket segi enam, ujung datar",
|
||||
"parts.family.socket_head_cap_screw": "Sekrup kepala soket (cap screw)",
|
||||
"parts.family.washer": "Ring datar (washer)",
|
||||
"parts.family.washer_sae": "Ring datar (washer), SAE (sempit)",
|
||||
"parts.family.washer_uss": "Ring datar (washer), USS (lebar)",
|
||||
"parts.holes": "Lubang (opsional)",
|
||||
"parts.placement_hint": "pilih satu lubang atau lebih untuk menempatkan part di masing-masing, atau OK menempatkannya di origin",
|
||||
"parts.system": "Sistem satuan",
|
||||
"parts.system.inch": "Inci",
|
||||
"parts.system.metric": "Metrik",
|
||||
"pattern.alignment_2": "Perataan 2",
|
||||
"pattern.alignment_hint": "Jumlah termasuk yang asli. Center menyeimbangkan salinan; Flip memilih sisi tambahan untuk total genap.",
|
||||
"pattern.direction_1": "Arah 1",
|
||||
@@ -2130,6 +2166,8 @@
|
||||
"prefs.button_left": "Seret kiri",
|
||||
"prefs.button_middle": "Seret tengah",
|
||||
"prefs.button_right": "Seret kanan",
|
||||
"prefs.carousel_next": "Berikutnya",
|
||||
"prefs.carousel_previous": "Sebelumnya",
|
||||
"prefs.choose_font": "Pilih file font…",
|
||||
"prefs.click": "Klik",
|
||||
"prefs.contact_shading": "Bayangan kontak lembut",
|
||||
@@ -2142,6 +2180,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Permukaan melengkung",
|
||||
"prefs.curves": "Permukaan melengkung",
|
||||
"prefs.cut_preview_tint": "Tampilkan material yang dihilangkan dengan warna merah",
|
||||
"prefs.cut_preview_tint_help": "Pratinjau mengisi material yang dihilangkan sebuah fitur, dan bentuk yang digantikannya, dengan bayangan merah tembus pandang. Jika mati, isian itu sepenuhnya transparan dan hanya garis luarnya yang digambar.",
|
||||
"prefs.dark_parts_shading": "Pertahankan bentuk part yang gelap",
|
||||
"prefs.dark_parts_shading_help": "Melakukan pewarnaan tampilan dalam cahaya linear dengan cahaya pengisi langit yang lembut, sehingga muka yang gelap tetap memiliki gradasi dan tidak menjadi hitam",
|
||||
"prefs.dead_zone_note": "Pita berarsir adalah zona mati: gerakan di dalamnya diabaikan.",
|
||||
@@ -2200,6 +2240,8 @@
|
||||
"prefs.interface_language": "Bahasa antarmuka",
|
||||
"prefs.interface_note": "Tampilan SolidCraft dan letak bagian-bagiannya.",
|
||||
"prefs.interval_minutes": "{n} mnt",
|
||||
"prefs.invert_curved_roll_arrows": "Balikkan panah roll melengkung",
|
||||
"prefs.invert_curved_roll_arrows_note": "Dua panah melengkung di sudut kubus memutar (roll) tampilan di dalam bidang layar. Aktif: panah searah jarum jam memutar berlawanan arah jarum jam dan panah berlawanan arah jarum jam memutar searah jarum jam. Mati: tiap panah memutar tampilan sesuai arah lengkungannya.",
|
||||
"prefs.invert_navigator_arrows": "Balikkan panah rotasi kubus tampilan",
|
||||
"prefs.invert_navigator_arrows_note": "Panah di sekeliling kubus memutar orbit kamera ke arah yang ditunjuknya, seperti di Fusion dan Inventor: panah di kiri menunjuk ke kanan dan mengayunkan kamera ke kanan. Mati: panah memutar kubus ke sisi tempatnya berada, sehingga panah kiri mengayunkan kamera ke kiri. Panah roll yang melengkung tidak terpengaruh.",
|
||||
"prefs.keep_work_on_tab_switch": "Biarkan dialog dan alat tetap terbuka saat berpindah desain",
|
||||
@@ -2259,6 +2301,8 @@
|
||||
"prefs.placement": "Penempatan",
|
||||
"prefs.prefer_free_sketch_points": "Utamakan titik sketsa yang dapat digerakkan saat titik saling tumpang tindih",
|
||||
"prefs.preview_choices": "Pratinjau pilihan Anda",
|
||||
"prefs.preview_tint_new_faces": "Warnai permukaan baru di pratinjau",
|
||||
"prefs.preview_tint_new_faces_help": "Pratinjau fitur yang mengubah sebuah bodi dapat menggambar permukaan yang ditambahkannya dengan warna pratinjau. Jika mati, bodi digambar apa adanya.",
|
||||
"prefs.print_face_highlight": "Sorotan permukaan cetak",
|
||||
"prefs.print_face_highlight_tip": "Seberapa kuat permukaan cetak sebuah bodi diwarnai dan diberi garis tepi di tampilan. 0 % tidak menampilkan sorotan.",
|
||||
"prefs.profile_active": "Profil aktif",
|
||||
@@ -2556,6 +2600,10 @@
|
||||
"render.base_color": "Warna dasar · sRGB",
|
||||
"render.base_metallic": "Fraksi metalik",
|
||||
"render.base_opaque_only": "Editor ini mempertahankan semantik bodi opak. Gunakan kontrol kaca eksplisit untuk transmisi optik.",
|
||||
"render.batch_all_shots": "Semua bidikan ({count})",
|
||||
"render.batch_empty": "Simpan sebuah bidikan sebelum merender semua bidikan.",
|
||||
"render.batch_limit": "Render Semua Bidikan dibatasi hingga 32 bidikan.",
|
||||
"render.batch_status": "{running} berjalan · {waiting} menunggu",
|
||||
"render.bloom_enable": "Tampilkan bloom",
|
||||
"render.bloom_help": "Cahaya kreatif khusus keluaran dari sorotan linear positif, disebar dengan tiga lintasan kotak ternormalisasi. Radius dalam piksel keluaran. Ditambahkan dalam cahaya tampilan linear sebelum sRGB; bukan lensa, efek volumetrik, atau peningkatan konvergensi. EXR mentah tidak berubah.",
|
||||
"render.bloom_opaque": "Bloom memerlukan keluaran opak. PNG transparan ditolak, tidak diratakan secara diam-diam.",
|
||||
@@ -2666,6 +2714,13 @@
|
||||
"render.choose_look": "Pilih look di Tampilan terlebih dahulu",
|
||||
"render.chromatic_offset": "Offset tepi kromatik",
|
||||
"render.clear_stage": "Hapus panggung tersimpan",
|
||||
"render.compare_divider": "Pembagi perbandingan",
|
||||
"render.compare_live": "Langsung",
|
||||
"render.compare_live_preview": "Pratinjau langsung",
|
||||
"render.compare_render": "Render {number}",
|
||||
"render.compare_side": "Render {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} d · {renderer} · ditangkap pada revisi {revision}",
|
||||
"render.compare_swap": "Tukar",
|
||||
"render.copy_request": "Salin JSON",
|
||||
"render.cpu_aa_help": "Sampel spasial: lebih banyak sampel memperbaiki tepi dan menambah waktu render.",
|
||||
"render.cpu_area_add": "Tambah lampu persegi panjang CPU",
|
||||
@@ -3415,6 +3470,10 @@
|
||||
"render.result_retained": "Menampilkan gambar selesai terakhir; render yang lebih baru belum menggantikannya.",
|
||||
"render.result_sequence": "Checkpoint",
|
||||
"render.result_title": "Hasil Render",
|
||||
"render.results_choose": "Hasil yang dipertahankan",
|
||||
"render.results_label": "Render {number} · {shot} · {seconds} d",
|
||||
"render.results_stale_label": "{result} [usang]",
|
||||
"render.results_unnamed": "Gambar",
|
||||
"render.return_design": "Kembali ke Desain",
|
||||
"render.review_stale": "Tinjauan ini sudah usang. Selesaikan atau periksa lagi sebelum menerapkan.",
|
||||
"render.ribbon_aging": "Penuaan",
|
||||
@@ -3898,10 +3957,7 @@
|
||||
"the two origins are on the same component": "kedua origin berada pada komponen yang sama",
|
||||
"thread.automatic_size": "Ukuran otomatis",
|
||||
"thread.designation": "Penunjukan",
|
||||
"thread.direction": "Arah ulir",
|
||||
"thread.iso_metric": "ISO metrik",
|
||||
"thread.left_hand": "Ulir kiri",
|
||||
"thread.right_hand": "Ulir kanan",
|
||||
"thread.select_size": "Pilih ukuran",
|
||||
"timeline.delete_after_marker_one": "Hapus 1 Fitur Setelah Penanda Riwayat",
|
||||
"timeline.delete_after_marker_other": "Hapus {count} Fitur Setelah Penanda Riwayat",
|
||||
@@ -4075,6 +4131,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Jalankan ini di terminal, lalu tekan Hubungkan lagi:",
|
||||
"ui.agent_chat_auth_title": "Masuk ke agen",
|
||||
"ui.agent_chat_cancel": "Batal",
|
||||
"ui.agent_chat_cancel_timeout": "Agen tidak merespons Berhenti dan terputus. Hubungkan lagi untuk melanjutkan.",
|
||||
"ui.agent_chat_cannot_start": "Terpasang, tetapi tidak dapat dimulai: {error}",
|
||||
"ui.agent_chat_changes": "Perubahan: {n}, satu langkah urung",
|
||||
"ui.agent_chat_changes_n": "{n} perubahan",
|
||||
@@ -4084,11 +4141,13 @@
|
||||
"ui.agent_chat_command": "Perintah agen",
|
||||
"ui.agent_chat_connect": "Hubungkan",
|
||||
"ui.agent_chat_copy": "Salin",
|
||||
"ui.agent_chat_copy_message": "Salin pesan",
|
||||
"ui.agent_chat_ctx_bodies": "Bodi",
|
||||
"ui.agent_chat_ctx_elements": "Elemen",
|
||||
"ui.agent_chat_ctx_features": "Fitur",
|
||||
"ui.agent_chat_ctx_more": "+{n} lagi",
|
||||
"ui.agent_chat_ctx_other": "Lainnya",
|
||||
"ui.agent_chat_custom_heading": "Model kustom",
|
||||
"ui.agent_chat_custom_hint": "Program apa pun yang berbicara Agent Client Protocol lewat stdin dan stdout. Tidak dijalankan oleh shell.",
|
||||
"ui.agent_chat_denied": "Ditolak",
|
||||
"ui.agent_chat_deny": "Tolak",
|
||||
@@ -4132,8 +4191,16 @@
|
||||
"ui.agent_chat_mode_all": "Semua",
|
||||
"ui.agent_chat_mode_ask": "Tanya",
|
||||
"ui.agent_chat_mode_edits": "Edit",
|
||||
"ui.agent_chat_model_custom": "Gunakan kustom: {model}",
|
||||
"ui.agent_chat_model_no_tools": "{model} tidak dapat memakai alat SolidCraft, jadi hanya bisa mengobrol. Pilih model yang mendukung alat.",
|
||||
"ui.agent_chat_model_rejected": "Agen tidak menerima {model}: {error}",
|
||||
"ui.agent_chat_model_search": "Cari model…",
|
||||
"ui.agent_chat_model_tip": "Model yang dipakai agen. Pilihan Anda diingat untuk agen ini.",
|
||||
"ui.agent_chat_models_failed": "Tidak dapat mencantumkan model: {error}",
|
||||
"ui.agent_chat_models_heading": "Model yang tersedia",
|
||||
"ui.agent_chat_models_listing": "Mencantumkan model…",
|
||||
"ui.agent_chat_models_none": "Endpoint tidak mencantumkan model",
|
||||
"ui.agent_chat_models_not_a_list": "Jawabannya bukan daftar model",
|
||||
"ui.agent_chat_new": "Chat baru",
|
||||
"ui.agent_chat_new_confirm": "Mulai chat baru? Chat saat ini akan dibersihkan.",
|
||||
"ui.agent_chat_next_picture": "Gambar berikutnya",
|
||||
@@ -4407,14 +4474,21 @@
|
||||
"ui.construction.contact_hint": "Garis singgung: pilih lingkaran/busur atau spline/kurva konik terbuka, lalu titik singgung (kurva bentuk bebas: titik ujung)",
|
||||
"ui.construction.external_hint": "Garis singgung: pilih lingkaran atau busur, sebuah titik luar, lalu solusi yang diinginkan",
|
||||
"ui.construction.free_hint": "Garis bantu: klik dua titik; klik kanan untuk mengulang",
|
||||
"ui.construction.normal_needs_length": "Garis tak hingga selalu berupa garis bantu: ketik panjang untuk menggambar garis normal",
|
||||
"ui.construction.point_hint": "Garis bantu: klik titik yang akan dilalui",
|
||||
"ui.construction.reference_hint": "Garis bantu: pilih garis lurus, lalu sebuah titik; klik kanan untuk mengganti referensi",
|
||||
"ui.construction.ribbon_label": "Garis bantu",
|
||||
"ui.construction.side_hint": "Garis bantu: klik sisi tempat garis mengarah; klik kanan untuk mengulang",
|
||||
"ui.context_menu": "Menu konteks",
|
||||
"ui.continuity_crease": "Lipatan",
|
||||
"ui.continuity_curvature": "Kelengkungan",
|
||||
"ui.continuity_curvature_change": "Perubahan kelengkungan",
|
||||
"ui.continuity_g0": "Lipatan G0",
|
||||
"ui.continuity_g1": "Tangen G1",
|
||||
"ui.continuity_g2": "Kelengkungan G2",
|
||||
"ui.continuity_gap": "Celah",
|
||||
"ui.continuity_position": "Posisi",
|
||||
"ui.continuity_tangent": "Tangen",
|
||||
"ui.copy": "Salin",
|
||||
"ui.corner": "Sudut",
|
||||
"ui.corners": "Sudut",
|
||||
@@ -4675,6 +4749,7 @@
|
||||
"ui.hideOrigin": "Sembunyikan Origin",
|
||||
"ui.hidePlanes": "Sembunyikan Bidang",
|
||||
"ui.hideProfile": "Sembunyikan Profil",
|
||||
"ui.hide_component_history": "Sembunyikan Riwayatnya",
|
||||
"ui.hide_origin_by_default": "Sembunyikan origin secara default",
|
||||
"ui.hide_origin_note": "Desain baru dimulai dengan folder Origin tersembunyi. Desain mempertahankan pengaturan Origin-nya sendiri setelah Anda menampilkan atau menyembunyikannya.",
|
||||
"ui.history_back": "Mundur satu langkah",
|
||||
@@ -4682,6 +4757,7 @@
|
||||
"ui.history_edge_hint": "klik dua kali tepinya",
|
||||
"ui.history_first": "Ke awal",
|
||||
"ui.history_forward": "Maju satu langkah",
|
||||
"ui.history_hide_hidden": "Sembunyikan Komponen Tersembunyi",
|
||||
"ui.history_hide_names": "Sembunyikan nama",
|
||||
"ui.history_last": "Ke akhir",
|
||||
"ui.history_marker_tip": "Penanda riwayat: seret untuk memundurkan model; fitur baru ditaruh di sini",
|
||||
@@ -4691,10 +4767,12 @@
|
||||
"ui.history_play_button_show": "Tampilkan Tombol Putar",
|
||||
"ui.history_play_finish_action": "Selesaikan aksi saat ini sebelum memutar Riwayat",
|
||||
"ui.history_play_roll_back": "Pindahkan penanda Riwayat ke belakang sebelum memutar",
|
||||
"ui.history_show_hidden": "Tampilkan Komponen Tersembunyi",
|
||||
"ui.history_show_names": "Tampilkan nama",
|
||||
"ui.history_sketch_listed_under": "Terdaftar di bawah {feature}",
|
||||
"ui.history_title": "Riwayat",
|
||||
"ui.history_toggle_names": "Tampilkan atau sembunyikan nama fitur",
|
||||
"ui.history_waits_for_editor": "Urungkan, Ulangi, dan Riwayat menunggu hingga editor fitur terbuka",
|
||||
"ui.hole": "Lubang",
|
||||
"ui.hole_tap_type": "Tipe Tap Lubang",
|
||||
"ui.hole_type": "Tipe Lubang",
|
||||
@@ -4710,6 +4788,7 @@
|
||||
"ui.insert_design.saved_file": "{name} memiliki perubahan yang belum disimpan, sehingga file tersimpannya yang disisipkan. Simpan desain itu, lalu jalankan Perbarui Tautan untuk memasukkan perubahannya.",
|
||||
"ui.insert_design.unsaved": "Perubahan belum disimpan",
|
||||
"ui.inspect": "Inspeksi",
|
||||
"ui.instances": "Instans",
|
||||
"ui.intersect": "Irisan",
|
||||
"ui.isolate": "Isolasi",
|
||||
"ui.join": "Gabung",
|
||||
@@ -4974,6 +5053,7 @@
|
||||
"ui.showDimension": "Tampilkan Dimensi",
|
||||
"ui.showOrigin": "Tampilkan Origin",
|
||||
"ui.showPlanes": "Tampilkan Bidang",
|
||||
"ui.show_component_history": "Tampilkan Riwayatnya",
|
||||
"ui.show_constraints": "Batasan",
|
||||
"ui.show_construction_geometries": "Geometri Konstruksi",
|
||||
"ui.show_dimensions": "Dimensi",
|
||||
@@ -5156,7 +5236,7 @@
|
||||
"update.check_unavailable": "Pemeriksaan pembaruan tidak tersedia untuk instalasi ini.",
|
||||
"update.checking": "Memeriksa…",
|
||||
"update.copy": "Salin",
|
||||
"update.dmg_opened": "Disk image terbuka dan terverifikasi: seret SolidCraft ke Applications, ganti salinan lama, lalu mulai ulang SolidCraft.",
|
||||
"update.dmg_opened": "Disk image terbuka dan terverifikasi: keluar dari SolidCraft, seret SolidCraft yang baru ke Applications, ganti salinan lama, lalu buka SolidCraft lagi.",
|
||||
"update.download": "Unduh",
|
||||
"update.download_open": "Unduh dan buka",
|
||||
"update.downloading": "Mengunduh dan memverifikasi… {done} dari {total} MB",
|
||||
@@ -5200,6 +5280,7 @@
|
||||
"web.open_link": "Buka desain tertaut?",
|
||||
"web.open_link_confirm": "Ada perubahan yang belum disimpan. Buka desain tertaut di tab baru?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ pilih · Enter / Tab lengkapi · Esc tutup",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ pilih · Enter menerapkan nilai ini",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ pilih · Enter menerapkan yang Anda ketik · Esc tutup",
|
||||
"⌘Enter continues on GitHub.": "⌘Enter melanjutkan di GitHub."
|
||||
}
|
||||
|
||||
+86
-4
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "Rettangolare",
|
||||
"Rectangular Tube": "Tubo rettangolare",
|
||||
"Reference face": "Faccia di riferimento",
|
||||
"Reference plane": "Piano di riferimento",
|
||||
"Relief Depth": "Profondità di scarico",
|
||||
"Relief Width": "Larghezza di scarico",
|
||||
"Remove Tools": "Rimuovi corpi utensile",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "Sezione",
|
||||
"Section (optional)": "Sezione (facoltativa)",
|
||||
"Section position": "Posizione della sezione",
|
||||
"Sections": "Sezioni",
|
||||
"Sections (one sketch each)": "Sezioni (uno schizzo ciascuna)",
|
||||
"Select Other": "Seleziona altro",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Seleziona altro: tieni premuto il pulsante sinistro senza muovere il puntatore sul modello (o fai clic destro › Seleziona altro…) per elencare gli oggetti sotto il cursore; fai di nuovo clic nello stesso punto per selezionare l'oggetto successivo. Fai doppio clic con il pulsante centrale per tornare alla vista iniziale (Preferenze › Navigazione).",
|
||||
@@ -859,6 +861,7 @@
|
||||
"cmd.component_extract": "Estrai in file",
|
||||
"cmd.component_finish_edit": "Termina modifica",
|
||||
"cmd.component_from_bodies": "Crea componenti da corpi",
|
||||
"cmd.component_hide_history": "Nascondi cronologia del componente",
|
||||
"cmd.component_insert_design": "Inserisci progetto",
|
||||
"cmd.component_links": "Elenca collegamenti",
|
||||
"cmd.component_list": "Elenca componenti",
|
||||
@@ -875,6 +878,7 @@
|
||||
"cmd.component_rename": "Rinomina componente",
|
||||
"cmd.component_replace_with_link": "Sostituisci con collegamento",
|
||||
"cmd.component_revert_position": "Ripristina posizione",
|
||||
"cmd.component_show_history": "Mostra cronologia del componente",
|
||||
"cmd.component_unlock": "Sblocca contenuto",
|
||||
"cmd.component_update_links": "Aggiorna collegamenti",
|
||||
"cmd.config_activate": "Attiva configurazione",
|
||||
@@ -885,6 +889,7 @@
|
||||
"cmd.config_rule": "Regola di configurazione",
|
||||
"cmd.config_rules": "Regole di configurazione",
|
||||
"cmd.config_table": "Mostra tabella delle configurazioni",
|
||||
"cmd.construct.plane.axis_point": "Piano per un asse e un punto",
|
||||
"cmd.construct_axis_edge": "Asse lungo uno spigolo",
|
||||
"cmd.construct_axis_normal_to_face": "Asse perpendicolare a una faccia",
|
||||
"cmd.construct_axis_through_cylinder": "Asse di cilindro/cono/toro",
|
||||
@@ -933,6 +938,11 @@
|
||||
"cmd.explode_show": "Mostra vista esplosa",
|
||||
"cmd.expr_evaluate": "Valuta espressione",
|
||||
"cmd.face_delete": "Elimina faccia",
|
||||
"cmd.file.export_3mf": "Esporta 3MF",
|
||||
"cmd.file.export_obj": "Esporta OBJ",
|
||||
"cmd.file.export_step": "Esporta STEP",
|
||||
"cmd.file.export_stl": "Esporta STL",
|
||||
"cmd.file.export_urdf": "Esporta URDF",
|
||||
"cmd.file_export": "Esporta",
|
||||
"cmd.file_insert_mesh": "Inserisci mesh",
|
||||
"cmd.file_insert_step": "Inserisci STEP",
|
||||
@@ -950,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "Attiva colori dei componenti",
|
||||
"cmd.inspect_colored_components_disable": "Disattiva colori dei componenti",
|
||||
"cmd.inspect_compare": "Confronta corpi",
|
||||
"cmd.inspect_compare_picture": "Confronta immagine",
|
||||
"cmd.inspect_continuity": "Analisi di continuità",
|
||||
"cmd.inspect_curvature_comb": "Analisi del pettine di curvatura",
|
||||
"cmd.inspect_curvature_map": "Analisi della mappa di curvatura",
|
||||
@@ -1040,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "Modifica nuvola di punti",
|
||||
"cmd.pointcloud_insert": "Inserisci nuvola di punti",
|
||||
"cmd.pointcloud_list": "Nuvole di punti",
|
||||
"cmd.render.compare.divider": "Sposta divisore del confronto di rendering",
|
||||
"cmd.render.result.discard": "Scarta risultato di rendering",
|
||||
"cmd.render.result.select": "Seleziona risultato di rendering",
|
||||
"cmd.render_aging_describe": "Invecchiamento a strati delle superfici",
|
||||
"cmd.render_backends": "Motori di rendering",
|
||||
"cmd.render_batch": "Renderizza tutte le inquadrature",
|
||||
@@ -1254,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "Sposta marcatore della sequenza temporale",
|
||||
"cmd.timeline_stop": "Arresta riproduzione cronologia",
|
||||
"cmd.timeline_suppress": "Sospendi lavorazione",
|
||||
"cmd.ui.assistant": "Mostra/Nascondi assistente",
|
||||
"cmd.view.render_browser": "Struttura del rendering",
|
||||
"cmd.view.render_dock": "Sposta pannello di rendering",
|
||||
"cmd.view.render_focus": "Vista focalizzata sul rendering",
|
||||
@@ -1288,9 +1303,11 @@
|
||||
"cmd.view_tool": "Strumento di navigazione",
|
||||
"cmd.view_top": "Vista dall'alto",
|
||||
"cmd.view_visual_style": "Stile di visualizzazione",
|
||||
"color.blue": "Blu",
|
||||
"color.copy": "Fai clic per copiare i valori del colore",
|
||||
"color.edit": "Fai clic per modificare il colore",
|
||||
"color.gamma": "Valori del colore mostrati nello spazio gamma 0-255",
|
||||
"color.green": "Verde",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "Inserisci #rrggbb o #rrggbbaa; con sei cifre l'opacità resta invariata",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1298,6 +1315,7 @@
|
||||
"color.linear": "Valori del colore mostrati nello spazio lineare 0-1",
|
||||
"color.recent": "Colori recenti",
|
||||
"color.recent_help": "I colori scelti in qualsiasi campo colore compaiono qui",
|
||||
"color.red": "Rosso",
|
||||
"color.saturation": "Saturazione (percentuale)",
|
||||
"color.saturation_value": "Saturazione e valore",
|
||||
"color.selected": "Colore selezionato",
|
||||
@@ -1305,6 +1323,8 @@
|
||||
"color.step_up": "Aumenta di un passo",
|
||||
"color.value": "Valore, la luminosità (percentuale)",
|
||||
"config.column_filter_empty": "Nessuna colonna corrispondente",
|
||||
"config.rule_parameter_columns": "Sono elencati solo i parametri che hanno una colonna nella tabella. Aggiungine una con Aggiungi colonna (+) per usarla qui.",
|
||||
"config.rule_suppression_columns": "Sono elencate solo le lavorazioni che hanno una colonna Sospendi nella tabella. Aggiungine una con Aggiungi colonna (+) per comandarle da qui.",
|
||||
"contact.components_count": "{count} componenti",
|
||||
"contact.enabled_label": "abilitato",
|
||||
"coupling.gear_1": "Ruota dentata 1",
|
||||
@@ -1353,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "Seleziona corpi o lavorazioni: uno schizzo non è nessuno dei due. Ripeti le sue curve con Ripetizione o Specchia dello schizzo stesso, oppure estrudilo prima.",
|
||||
"dialog.select": "↖ Seleziona",
|
||||
"dialog.selected_count": "↖ Selezionati: {count}",
|
||||
"dialog.selection_constraint": "Vincolo {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · curva {id}",
|
||||
"dialog.selection_guidance": "Seleziona un dato di ingresso per modificarlo. Fai clic sugli oggetti selezionati per rimuoverli; anche Ctrl/Cmd-clic ne alterna la selezione. Controlla l'anteprima, poi premi OK.",
|
||||
"dialog.selection_midpoint": "Punto medio · {id}",
|
||||
"dialog.selection_profile": "{sketch} · profilo {number}",
|
||||
"dialog.selection_step": "{step} di {total}: seleziona {input}",
|
||||
"dialog.step": "Passo",
|
||||
"document.changed_on_disk": "{name} è stato modificato sul disco",
|
||||
@@ -1436,6 +1460,7 @@
|
||||
"help.construct.axis.two_points": "Crea un asse di costruzione passante per due punti, vertici o punti dello schizzo. Usalo per un asse che collega due posizioni note.",
|
||||
"help.construct.plane.along_path": "Crea un piano di costruzione perpendicolare a uno spigolo o a una curva, in una posizione lungo di essa. Usalo per disegnare il profilo di uno sweep o una sezione di un percorso.",
|
||||
"help.construct.plane.at_angle": "Crea un piano di costruzione ruotato di un angolo rispetto a un piano o a una faccia, attorno a un asse o a uno spigolo. Usalo per schizzi inclinati, ad esempio una staffa angolata.",
|
||||
"help.construct.plane.axis_point": "Crea un piano di costruzione che contiene un asse e un punto esterno all'asse. Seleziona un'origine o un asse di costruzione, uno spigolo rettilineo o una linea dello schizzo, poi un vertice, un punto dello schizzo o un punto di costruzione. Un punto sull'asse non può definire un piano univoco.",
|
||||
"help.construct.plane.midplane": "Crea un piano di costruzione a metà tra due piani o facce piane, oppure che li biseca dove si incontrano ad angolo. Usalo come piano di simmetria per specchiare.",
|
||||
"help.construct.plane.offset": "Crea un piano di costruzione parallelo a un piano o a una faccia piana, a una certa distanza o passante per un punto. Usalo per disegnare a un'altezza stabilita sopra una faccia.",
|
||||
"help.construct.plane.perpendicular": "Crea un piano di costruzione che contiene una linea o uno spigolo ed è perpendicolare a un piano o a una faccia. Usalo per disegnare in verticale sul modello lungo uno spigolo.",
|
||||
@@ -1812,6 +1837,8 @@
|
||||
"joint_move.blocked_hint": "Controlla il tipo di giunto (ad esempio Rigido), il fissaggio a terra, i limiti del giunto e i valori comandati da parametri.",
|
||||
"joint_move.blocked_title": "Impossibile spostare questa parte",
|
||||
"joint_move.coarse": "Passo di rotazione vicino all'asse (°)",
|
||||
"joint_move.conflict": "Un giunto di questa parte è in conflitto con gli altri: {joint}.",
|
||||
"joint_move.conflict_hint": "Modifica, sospendi o elimina il giunto in conflitto, così gli altri giunti potranno posizionare la parte.",
|
||||
"joint_move.distance": "Distanza dall'inizio",
|
||||
"joint_move.done": "Fatto",
|
||||
"joint_move.fine": "Passo di rotazione lontano dall'asse (°)",
|
||||
@@ -1911,6 +1938,8 @@
|
||||
"motion.track_name": "Traccia {number}",
|
||||
"nav.alias.help": "Shift+Alt+trascinamento sinistro orbita, Shift+Alt+centrale fa la panoramica, Shift+Alt+destro fa lo zoom; anche il trascinamento centrale fa la panoramica e il destro orbita",
|
||||
"nav.blender.help": "Il trascinamento centrale orbita, Shift+centrale fa la panoramica, Ctrl+centrale fa lo zoom; il trascinamento destro lascia ferma la vista",
|
||||
"nav.creo.help": "Il trascinamento centrale orbita, Shift+centrale fa la panoramica, il trascinamento verticale con Ctrl+centrale fa lo zoom (verso il basso per avvicinare); scorri la rotellina per fare lo zoom",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "Mouse e mouse 3D ruotano oltre entrambi i poli e conservano il rollio. L'aggancio degli assi con Shift è disattivato.",
|
||||
"nav.free_orbit.label": "Orbita libera",
|
||||
"nav.free_orbit.snap_help": "Mouse e mouse 3D ruotano oltre entrambi i poli e conservano il rollio. Tieni premuto Shift durante il trascinamento dell'orbita per agganciare la direzione della vista e il rollio.",
|
||||
@@ -2063,12 +2092,19 @@
|
||||
"parts.family.button_head_cap_screw": "Vite a testa bombata con esagono incassato",
|
||||
"parts.family.dowel_pin": "Spina cilindrica",
|
||||
"parts.family.hex_bolt": "Vite a testa esagonale",
|
||||
"parts.family.hex_cap_screw_inch": "Vite a testa esagonale (pollici)",
|
||||
"parts.family.hex_nut": "Dado esagonale",
|
||||
"parts.family.hex_nut_inch": "Dado esagonale (pollici)",
|
||||
"parts.family.set_screw": "Grano a esagono incassato, punta piana",
|
||||
"parts.family.socket_head_cap_screw": "Vite a testa cilindrica con esagono incassato",
|
||||
"parts.family.washer": "Rondella piana",
|
||||
"parts.family.washer_sae": "Rondella piana, SAE (stretta)",
|
||||
"parts.family.washer_uss": "Rondella piana, USS (larga)",
|
||||
"parts.holes": "Fori (facoltativo)",
|
||||
"parts.placement_hint": "scegli uno o più fori per alloggiare una parte in ciascuno, oppure OK la colloca nell'origine",
|
||||
"parts.system": "Sistema di unità",
|
||||
"parts.system.inch": "Pollici",
|
||||
"parts.system.metric": "Metrico",
|
||||
"pattern.alignment_2": "Allineamento 2",
|
||||
"pattern.alignment_hint": "Il conteggio include l'originale. Centro distribuisce le copie sui due lati; Inverti sceglie il lato con la copia in più per totali pari.",
|
||||
"pattern.direction_1": "Direzione 1",
|
||||
@@ -2121,6 +2157,8 @@
|
||||
"prefs.bottom_centre": "In basso al centro",
|
||||
"prefs.bottom_right": "In basso a destra",
|
||||
"prefs.browser": "Struttura",
|
||||
"prefs.browser_components_open": "Componenti aperti all'inizio",
|
||||
"prefs.browser_components_open_note": "Ogni componente nel browser inizia aperto, come prima, invece che chiuso finché non lo apri.",
|
||||
"prefs.browser_eye_left": "Occhio accanto alla freccia di espansione",
|
||||
"prefs.browser_eye_left_note": "Le righe della Struttura mostrano l'occhio mostra/nascondi accanto alla freccia di espansione, come prima, invece che in una colonna all'estremità destra delle righe.",
|
||||
"prefs.browser_reveal_on_hover": "Mostra al passaggio del puntatore",
|
||||
@@ -2130,6 +2168,8 @@
|
||||
"prefs.button_left": "Trascinamento sinistro",
|
||||
"prefs.button_middle": "Trascinamento centrale",
|
||||
"prefs.button_right": "Trascinamento destro",
|
||||
"prefs.carousel_next": "Successivo",
|
||||
"prefs.carousel_previous": "Precedente",
|
||||
"prefs.choose_font": "Scegli il file del carattere…",
|
||||
"prefs.click": "Clic",
|
||||
"prefs.contact_shading": "Ombreggiatura di contatto morbida",
|
||||
@@ -2142,6 +2182,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Superfici curve",
|
||||
"prefs.curves": "Superfici curve",
|
||||
"prefs.cut_preview_tint": "Mostra in rosso il materiale rimosso",
|
||||
"prefs.cut_preview_tint_help": "Un'anteprima riempie il materiale che una lavorazione rimuove, e la forma che sostituisce, con un fantasma rosso semitrasparente. Se disattivato, questi riempimenti sono completamente trasparenti e vengono disegnati solo i loro contorni.",
|
||||
"prefs.dark_parts_shading": "Mantieni la forma delle parti scure",
|
||||
"prefs.dark_parts_shading_help": "Ombreggia la vista in luce lineare con un morbido riempimento di luce dal cielo, così le facce scure mantengono una sfumatura invece di diventare nere",
|
||||
"prefs.dead_zone_note": "La fascia tratteggiata è la zona morta: il movimento al suo interno viene ignorato.",
|
||||
@@ -2200,6 +2242,8 @@
|
||||
"prefs.interface_language": "Lingua dell'interfaccia",
|
||||
"prefs.interface_note": "Aspetto di SolidCraft e disposizione dei suoi elementi.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_curved_roll_arrows": "Inverti le frecce curve di rollio",
|
||||
"prefs.invert_curved_roll_arrows_note": "Le due frecce curve all'angolo del cubo fanno ruotare la vista nel piano dello schermo. Attivato: la freccia oraria ruota in senso antiorario e la freccia antioraria ruota in senso orario. Disattivato: ogni freccia ruota la vista nel verso in cui si curva.",
|
||||
"prefs.invert_navigator_arrows": "Inverti le frecce di rotazione del cubo della vista",
|
||||
"prefs.invert_navigator_arrows_note": "Le frecce attorno al cubo fanno orbitare la camera nella direzione in cui puntano, come in Fusion e Inventor: la freccia a sinistra punta a destra e sposta la camera a destra. Disattivato: una freccia ruota il cubo verso il lato in cui si trova, quindi la freccia sinistra sposta la camera a sinistra. Le frecce curve di rollio non sono interessate.",
|
||||
"prefs.keep_work_on_tab_switch": "Mantieni aperti finestre di dialogo e strumenti quando cambi progetto",
|
||||
@@ -2259,6 +2303,8 @@
|
||||
"prefs.placement": "Posizione",
|
||||
"prefs.prefer_free_sketch_points": "Preferisci i punti dello schizzo mobili quando i punti si sovrappongono",
|
||||
"prefs.preview_choices": "Anteprima delle tue scelte",
|
||||
"prefs.preview_tint_new_faces": "Colora la nuova superficie in un'anteprima",
|
||||
"prefs.preview_tint_new_faces_help": "L'anteprima di una lavorazione che modifica un corpo può disegnare la superficie aggiunta con il colore dell'anteprima. Se disattivato, il corpo viene disegnato com'è.",
|
||||
"prefs.print_face_highlight": "Evidenziazione della faccia di stampa",
|
||||
"prefs.print_face_highlight_tip": "Intensità della tinta e del contorno della faccia di stampa di un corpo nella vista. 0 % non mostra evidenziazione.",
|
||||
"prefs.profile_active": "Profilo attivo",
|
||||
@@ -2556,6 +2602,10 @@
|
||||
"render.base_color": "Colore di base · sRGB",
|
||||
"render.base_metallic": "Frazione metallica",
|
||||
"render.base_opaque_only": "Questo editor mantiene la semantica dei corpi opachi. Per la trasmissione ottica usa i controlli espliciti del vetro.",
|
||||
"render.batch_all_shots": "Tutte le inquadrature ({count})",
|
||||
"render.batch_empty": "Salva un'inquadratura prima di renderizzare tutte le inquadrature.",
|
||||
"render.batch_limit": "Il rendering di tutte le inquadrature è limitato a 32 inquadrature.",
|
||||
"render.batch_status": "{running} in corso · {waiting} in attesa",
|
||||
"render.bloom_enable": "Bloom di visualizzazione",
|
||||
"render.bloom_help": "Alone creativo solo in uscita, ricavato dalle alte luci lineari positive e diffuso con tre passate box normalizzate. Il raggio è in pixel di uscita. Viene aggiunto nella luce di visualizzazione lineare prima della conversione sRGB; non è un effetto di lente o volumetrico né un miglioramento della convergenza. L'EXR grezzo resta invariato.",
|
||||
"render.bloom_opaque": "Il bloom richiede un'uscita opaca. Il PNG trasparente viene rifiutato, non appiattito in silenzio.",
|
||||
@@ -2666,6 +2716,13 @@
|
||||
"render.choose_look": "Scegli prima un aspetto in Aspetti",
|
||||
"render.chromatic_offset": "Scostamento cromatico dei bordi",
|
||||
"render.clear_stage": "Rimuovi la scenografia salvata",
|
||||
"render.compare_divider": "Divisore del confronto",
|
||||
"render.compare_live": "Live",
|
||||
"render.compare_live_preview": "Anteprima dal vivo",
|
||||
"render.compare_render": "Rendering {number}",
|
||||
"render.compare_side": "Rendering {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} s · {renderer} · acquisito alla revisione {revision}",
|
||||
"render.compare_swap": "Scambia",
|
||||
"render.copy_request": "Copia JSON",
|
||||
"render.cpu_aa_help": "Campioni spaziali: più campioni migliorano i bordi e aumentano il tempo di rendering.",
|
||||
"render.cpu_area_add": "Aggiungi luce rettangolare CPU",
|
||||
@@ -3415,6 +3472,10 @@
|
||||
"render.result_retained": "Viene mostrata l'ultima immagine completata; un rendering più recente non l'ha ancora sostituita.",
|
||||
"render.result_sequence": "Punto di ripristino",
|
||||
"render.result_title": "Risultato del rendering",
|
||||
"render.results_choose": "Risultati conservati",
|
||||
"render.results_label": "Rendering {number} · {shot} · {seconds} s",
|
||||
"render.results_stale_label": "{result} [obsoleto]",
|
||||
"render.results_unnamed": "Immagine",
|
||||
"render.return_design": "Torna a Progettazione",
|
||||
"render.review_stale": "Questa revisione è obsoleta. Risolvi o verifica di nuovo prima di applicare.",
|
||||
"render.ribbon_aging": "Invecchiamento",
|
||||
@@ -3898,10 +3959,7 @@
|
||||
"the two origins are on the same component": "le due origini sono sullo stesso componente",
|
||||
"thread.automatic_size": "Dimensione automatica",
|
||||
"thread.designation": "Designazione",
|
||||
"thread.direction": "Verso del filetto",
|
||||
"thread.iso_metric": "Metrica ISO",
|
||||
"thread.left_hand": "Sinistro",
|
||||
"thread.right_hand": "Destro",
|
||||
"thread.select_size": "Seleziona dimensione",
|
||||
"timeline.delete_after_marker_one": "Elimina 1 lavorazione dopo il marcatore della cronologia",
|
||||
"timeline.delete_after_marker_other": "Elimina {count} lavorazioni dopo il marcatore della cronologia",
|
||||
@@ -4075,6 +4133,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Esegui questo comando in un terminale, poi premi di nuovo Connetti:",
|
||||
"ui.agent_chat_auth_title": "Accedi all'agente",
|
||||
"ui.agent_chat_cancel": "Annulla",
|
||||
"ui.agent_chat_cancel_timeout": "L'agente non ha risposto ad Arresta ed è stato disconnesso. Riconnetti per continuare.",
|
||||
"ui.agent_chat_cannot_start": "Installato, ma non si avvia: {error}",
|
||||
"ui.agent_chat_changes": "Modifiche: {n}, un solo passo di annullamento",
|
||||
"ui.agent_chat_changes_n": "{n} modifiche",
|
||||
@@ -4084,11 +4143,13 @@
|
||||
"ui.agent_chat_command": "Comando dell'agente",
|
||||
"ui.agent_chat_connect": "Connetti",
|
||||
"ui.agent_chat_copy": "Copia",
|
||||
"ui.agent_chat_copy_message": "Copia messaggio",
|
||||
"ui.agent_chat_ctx_bodies": "Corpi",
|
||||
"ui.agent_chat_ctx_elements": "Elementi",
|
||||
"ui.agent_chat_ctx_features": "Lavorazioni",
|
||||
"ui.agent_chat_ctx_more": "+{n} altri",
|
||||
"ui.agent_chat_ctx_other": "Altro",
|
||||
"ui.agent_chat_custom_heading": "Modello personalizzato",
|
||||
"ui.agent_chat_custom_hint": "Qualsiasi programma che comunica tramite Agent Client Protocol su stdin e stdout. Non viene eseguito da una shell.",
|
||||
"ui.agent_chat_denied": "Negato",
|
||||
"ui.agent_chat_deny": "Nega",
|
||||
@@ -4132,8 +4193,16 @@
|
||||
"ui.agent_chat_mode_all": "Tutto",
|
||||
"ui.agent_chat_mode_ask": "Chiedi",
|
||||
"ui.agent_chat_mode_edits": "Modifiche",
|
||||
"ui.agent_chat_model_custom": "Usa personalizzato: {model}",
|
||||
"ui.agent_chat_model_no_tools": "{model} non può usare gli strumenti di SolidCraft, quindi può solo chattare. Scegli un modello con supporto per gli strumenti.",
|
||||
"ui.agent_chat_model_rejected": "L'agente non ha accettato {model}: {error}",
|
||||
"ui.agent_chat_model_search": "Cerca modelli…",
|
||||
"ui.agent_chat_model_tip": "Il modello usato dall'agente. La scelta viene memorizzata per questo agente.",
|
||||
"ui.agent_chat_models_failed": "Impossibile elencare i modelli: {error}",
|
||||
"ui.agent_chat_models_heading": "Modelli disponibili",
|
||||
"ui.agent_chat_models_listing": "Elenco dei modelli in corso…",
|
||||
"ui.agent_chat_models_none": "L'endpoint non elenca alcun modello",
|
||||
"ui.agent_chat_models_not_a_list": "La risposta non era un elenco di modelli",
|
||||
"ui.agent_chat_new": "Nuova chat",
|
||||
"ui.agent_chat_new_confirm": "Avviare una nuova chat? Quella corrente verrà cancellata.",
|
||||
"ui.agent_chat_next_picture": "Immagine successiva",
|
||||
@@ -4407,14 +4476,21 @@
|
||||
"ui.construction.contact_hint": "Tangente: seleziona un cerchio/arco o una spline/conica aperta, poi il punto di contatto (curve a forma libera: estremi)",
|
||||
"ui.construction.external_hint": "Tangente: seleziona un cerchio o un arco, un punto esterno, poi la soluzione desiderata",
|
||||
"ui.construction.free_hint": "Retta di costruzione: fai clic su due punti; clic destro per ricominciare",
|
||||
"ui.construction.normal_needs_length": "Una retta infinita è sempre di costruzione: digita una lunghezza per disegnare una linea normale",
|
||||
"ui.construction.point_hint": "Retta di costruzione: fai clic sul punto di passaggio",
|
||||
"ui.construction.reference_hint": "Retta di costruzione: seleziona una linea retta, poi un punto; clic destro per cambiare il riferimento",
|
||||
"ui.construction.ribbon_label": "Linee aus.",
|
||||
"ui.construction.side_hint": "Retta di costruzione: fai clic sul lato verso cui prosegue la retta; clic destro per ricominciare",
|
||||
"ui.context_menu": "Menu contestuale",
|
||||
"ui.continuity_crease": "Spigolo vivo",
|
||||
"ui.continuity_curvature": "Curvatura",
|
||||
"ui.continuity_curvature_change": "Variazione di curvatura",
|
||||
"ui.continuity_g0": "G0 spigolo vivo",
|
||||
"ui.continuity_g1": "G1 tangenza",
|
||||
"ui.continuity_g2": "G2 curvatura",
|
||||
"ui.continuity_gap": "Gioco",
|
||||
"ui.continuity_position": "Posizione",
|
||||
"ui.continuity_tangent": "Tangente",
|
||||
"ui.copy": "Copia",
|
||||
"ui.corner": "Angolo",
|
||||
"ui.corners": "Angoli",
|
||||
@@ -4675,6 +4751,7 @@
|
||||
"ui.hideOrigin": "Nascondi origine",
|
||||
"ui.hidePlanes": "Nascondi piani",
|
||||
"ui.hideProfile": "Nascondi profilo",
|
||||
"ui.hide_component_history": "Nascondi la sua cronologia",
|
||||
"ui.hide_origin_by_default": "Nascondi l'origine per impostazione predefinita",
|
||||
"ui.hide_origin_note": "I nuovi progetti partono con la cartella Origine nascosta. Un progetto conserva la propria impostazione dell'origine dopo che l'hai mostrata o nascosta.",
|
||||
"ui.history_back": "Indietro di un passaggio",
|
||||
@@ -4682,6 +4759,7 @@
|
||||
"ui.history_edge_hint": "fai doppio clic sullo spigolo",
|
||||
"ui.history_first": "Vai all’inizio",
|
||||
"ui.history_forward": "Avanti di un passaggio",
|
||||
"ui.history_hide_hidden": "Nascondi componenti nascosti",
|
||||
"ui.history_hide_names": "Nascondi i nomi",
|
||||
"ui.history_last": "Vai alla fine",
|
||||
"ui.history_marker_tip": "Marcatore della cronologia: trascinalo per far tornare indietro il modello; le nuove lavorazioni vanno qui",
|
||||
@@ -4691,10 +4769,12 @@
|
||||
"ui.history_play_button_show": "Mostra pulsante Riproduci",
|
||||
"ui.history_play_finish_action": "Termina l'azione corrente prima di riprodurre la cronologia",
|
||||
"ui.history_play_roll_back": "Sposta indietro il marcatore della cronologia prima di riprodurre",
|
||||
"ui.history_show_hidden": "Mostra componenti nascosti",
|
||||
"ui.history_show_names": "Mostra nomi",
|
||||
"ui.history_sketch_listed_under": "Elencato sotto {feature}",
|
||||
"ui.history_title": "Cronologia",
|
||||
"ui.history_toggle_names": "Mostra o nascondi i nomi delle lavorazioni",
|
||||
"ui.history_waits_for_editor": "Annulla, Ripeti e Cronologia restano in attesa finché l'editor della lavorazione non si è aperto",
|
||||
"ui.hole": "Foro",
|
||||
"ui.hole_tap_type": "Tipo di maschiatura del foro",
|
||||
"ui.hole_type": "Tipo di foro",
|
||||
@@ -4975,6 +5055,7 @@
|
||||
"ui.showDimension": "Mostra quota",
|
||||
"ui.showOrigin": "Mostra origine",
|
||||
"ui.showPlanes": "Mostra piani",
|
||||
"ui.show_component_history": "Mostra la sua cronologia",
|
||||
"ui.show_constraints": "Vincoli",
|
||||
"ui.show_construction_geometries": "Geometrie di costruzione",
|
||||
"ui.show_dimensions": "Quote",
|
||||
@@ -5157,7 +5238,7 @@
|
||||
"update.check_unavailable": "La verifica degli aggiornamenti non è disponibile per questa installazione.",
|
||||
"update.checking": "Controllo…",
|
||||
"update.copy": "Copia",
|
||||
"update.dmg_opened": "L'immagine disco è aperta e verificata: trascina SolidCraft in Applicazioni sostituendo la copia precedente, poi riavvia SolidCraft.",
|
||||
"update.dmg_opened": "L'immagine disco è aperta e verificata: esci da SolidCraft, trascina il nuovo SolidCraft in Applicazioni sostituendo la copia precedente, poi riapri SolidCraft.",
|
||||
"update.download": "Scarica",
|
||||
"update.download_open": "Scarica e apri",
|
||||
"update.downloading": "Download e verifica… {done} di {total} MB",
|
||||
@@ -5201,6 +5282,7 @@
|
||||
"web.open_link": "Aprire il progetto collegato?",
|
||||
"web.open_link_confirm": "Ci sono modifiche non salvate. Aprire il progetto collegato in una nuova scheda?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ scegli · Invio / Tab completa · Esc chiudi",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ scegli · Enter applica questo valore",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ scegli · Enter applica quanto digitato · Esc chiudi",
|
||||
"⌘Enter continues on GitHub.": "⌘Enter continua su GitHub."
|
||||
}
|
||||
|
||||
+188
-14
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "長方形",
|
||||
"Rectangular Tube": "角形鋼管",
|
||||
"Reference face": "参照面",
|
||||
"Reference plane": "参照平面",
|
||||
"Relief Depth": "リリーフ深さ",
|
||||
"Relief Width": "リリーフ幅",
|
||||
"Remove Tools": "ツールボディを削除",
|
||||
@@ -494,7 +495,8 @@
|
||||
"Rename Folder": "フォルダーの名前を変更",
|
||||
"Render Stage": "レンダリング段階",
|
||||
"Reopen designs": "デザインを再度開く",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "前回 SolidCraft を閉じたときに開いていたデザインを再度開く",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "前回開いていたデザインを再び開く",
|
||||
"Repair Part": "パーツを修復",
|
||||
"Replace existing library file": "既存のライブラリファイルを置き換える",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "バグを報告 — スクリーンショットを確認してGitHub下書きを作成",
|
||||
"Report copied. Keep this text to submit on GitHub when you are online.": "レポートをコピーしました。このテキストを保存し、オンラインになったらGitHubで送信してください。",
|
||||
@@ -789,6 +791,7 @@
|
||||
"browser.edit_canvas_title": "キャンバスを編集: {name}",
|
||||
"browser.edit_point_cloud_title": "点群を編集: {name}",
|
||||
"browser.move_title": "移動: {name}",
|
||||
"browser.needs_repair": "{name}: 修復が必要",
|
||||
"browser.parameters": "パラメータ ({count})",
|
||||
"browser.precision": "精度: {precision}",
|
||||
"browser.sketch_attachment_lost": "{name} (アタッチメント消失)",
|
||||
@@ -833,6 +836,7 @@
|
||||
"cmd.assembly_mass_properties": "質量特性",
|
||||
"cmd.body.print_face": "造形面",
|
||||
"cmd.body_rename": "ボディの名前を変更",
|
||||
"cmd.body_repair": "パーツを修復",
|
||||
"cmd.browser_collapse_all": "すべて折りたたむ",
|
||||
"cmd.browser_expand_all": "すべて展開",
|
||||
"cmd.browser_group": "新規グループ",
|
||||
@@ -917,6 +921,8 @@
|
||||
"cmd.diagnostics_crash_reports": "クラッシュレポート一覧",
|
||||
"cmd.diagnostics_crash_reports_clear": "クラッシュレポートを消去",
|
||||
"cmd.diagnostics_crash_test": "クラッシュテスト",
|
||||
"cmd.diagnostics_profile": "プロファイル",
|
||||
"cmd.diagnostics_validation": "検証",
|
||||
"cmd.doc_recover": "デザインを復元",
|
||||
"cmd.doc_recovery_discard": "復元デザインを破棄",
|
||||
"cmd.doc_recovery_list": "復元可能なデザイン",
|
||||
@@ -954,6 +960,7 @@
|
||||
"cmd.frame_profile_list": "形材ライブラリ",
|
||||
"cmd.frame_profile_properties": "形材のプロパティ",
|
||||
"cmd.inspect_accessibility": "アクセシビリティ解析",
|
||||
"cmd.inspect_analysis": "表示解析",
|
||||
"cmd.inspect_center_of_mass": "重心",
|
||||
"cmd.inspect_clearance": "クリアランス",
|
||||
"cmd.inspect_colored_components": "コンポーネントの色分けを有効化",
|
||||
@@ -977,6 +984,7 @@
|
||||
"cmd.joint_delete": "ジョイントを削除",
|
||||
"cmd.joint_drive": "ジョイントを駆動",
|
||||
"cmd.joint_edit": "ジョイントを編集",
|
||||
"cmd.joint_find_limits": "ジョイントの制限を検出",
|
||||
"cmd.joint_gear": "ギア",
|
||||
"cmd.joint_inspect": "ジョイントを検査",
|
||||
"cmd.joint_limits": "ジョイントの制限",
|
||||
@@ -1358,6 +1366,7 @@
|
||||
"crash.unclean_explanation": "次回以降の起動時に検出されました。正確な終了時刻は記録されていません。システムのシャットダウンや強制停止によっても、正常終了が記録されない場合があります。",
|
||||
"delete.and_more": "…ほか{count}件",
|
||||
"diagnostic.empty_boolean_component": "コンポーネント '{component}' に切り取りまたは交差の対象となるボディがありません。ツールはコンポーネント '{other}' の '{body}' と重なる可能性があります。そのコンポーネントをアクティブにするか、ボディを対象として選択してください",
|
||||
"diagnostic.reselect_reference": "{feature}: 参照 `{reference}` は選択したときのままでは存在しません。選択し直してください",
|
||||
"dialog.bodies_count": "{count} ボディ",
|
||||
"dialog.clearance_invalid": "クリアランスは 0 以上の長さにしてください",
|
||||
"dialog.compute": "計算",
|
||||
@@ -1423,6 +1432,14 @@
|
||||
"file_load.reading_percent": "ファイルを読み込み中... {percent}%",
|
||||
"file_load.rebuilding": "フィーチャーを再構築中: {done}/{total}",
|
||||
"file_load.recovering": "デザインを復元中",
|
||||
"file_load.step_build": "モデルを構築中",
|
||||
"file_load.step_feature": "フィーチャー {done}/{total}",
|
||||
"file_load.step_part": "パーツ {done}/{total}",
|
||||
"file_load.step_percent": "{percent}%",
|
||||
"file_load.step_read": "ファイルを読み込み中",
|
||||
"file_load.step_rebuild": "フィーチャーを再構築中",
|
||||
"file_load.step_repair": "パーツを確認・修復中",
|
||||
"file_load.step_show": "ビューを準備中",
|
||||
"file_load.stopping": "前の操作の停止を待っています...",
|
||||
"file_load.updating_links": "リンクを更新中",
|
||||
"file_load.waiting": "ファイル操作を待っています",
|
||||
@@ -1717,6 +1734,7 @@
|
||||
"help.timeline.delete": "選択したフィーチャをタイムラインから削除します。スケッチを削除すると、そこから作ったフィーチャも削除されます。不要になったステップを取り除くときに使います。",
|
||||
"help.timeline.dependents": "フィーチャーと一緒に削除されるフィーチャーと、それがないと失敗するフィーチャーを一覧にします。フィーチャーを削除または変更する前に使います。",
|
||||
"help.timeline.play": "残りのフィーチャを一つずつ再生し、再構築のたびに0.5秒間停止します。このコマンドをもう一度実行するかEscキーで停止し、「最後へ」で直ちに最後へ移動します。",
|
||||
"help.version": "バージョン {version}",
|
||||
"help.view.apply": "ビューをフロント、トップ、アイソメなどの標準方向、または保存した名前付きビューに回転します。固定した角度からモデルを見るときに使います。",
|
||||
"help.view.delete": "保存した名前付きビューを削除します。モデルは変更されません。",
|
||||
"help.view.fit": "モデル全体、または選択したものがビューいっぱいになるように、ズームと移動をします。モデルが視界から外れたときに使います。",
|
||||
@@ -1804,6 +1822,27 @@
|
||||
"home.start_hint": "デザインを作成するか、最近使ったファイルを開くか、サンプルをご覧ください。",
|
||||
"home.whats_new": "新機能",
|
||||
"home.yesterday": "昨日",
|
||||
"import_report.details": "詳細:",
|
||||
"import_report.file_one": "ファイルにほかに 1 件の問題がありました。",
|
||||
"import_report.file_other": "ファイルにほかに {count} 件の問題がありました。",
|
||||
"import_report.hint": "作成元のプログラムからこれらのパーツを書き出し直すと直ることがあります。詳細は「警告メッセージをコピー」で確認できます。",
|
||||
"import_report.left_out_one": "1 個の面を修復できず、欠けています。",
|
||||
"import_report.left_out_other": "{count} 個の面を修復できず、欠けています。",
|
||||
"import_report.needs_repair_one": "1 個のパーツはモデリングの前に修復が必要です。",
|
||||
"import_report.needs_repair_other": "{count} 個のパーツはモデリングの前に修復が必要です。",
|
||||
"import_report.not_imported_one": "1 個のパーツを読み込めませんでした。",
|
||||
"import_report.not_imported_other": "{count} 個のパーツを読み込めませんでした。",
|
||||
"import_report.not_shown_one": "1 個の面を描画できず、非表示です。",
|
||||
"import_report.not_shown_other": "{count} 個の面を描画できず、非表示です。",
|
||||
"import_report.open_one": "1 個のパーツは面の間にすき間があり、開いたボディとして読み込まれました。",
|
||||
"import_report.open_other": "{count} 個のパーツは面の間にすき間があり、開いたボディとして読み込まれました。",
|
||||
"import_report.refused_one": "1 個のパーツをソリッドにできず、読み込みませんでした。",
|
||||
"import_report.refused_other": "{count} 個のパーツをソリッドにできず、読み込みませんでした。",
|
||||
"import_report.repaired_one": "1 個のパーツの小さな不具合を修復しました。",
|
||||
"import_report.repaired_other": "{count} 個のパーツの小さな不具合を修復しました。",
|
||||
"import_report.solids": "{parts} 個のパーツのうち {solids} 個をソリッドとして読み込みました。",
|
||||
"import_report.surfaces_one": "1 個のパーツをソリッドにできず、サーフェスとして読み込みました。",
|
||||
"import_report.surfaces_other": "{count} 個のパーツをソリッドにできず、サーフェスとして読み込みました。",
|
||||
"install.button_tip": "SolidCraft をアプリとしてインストールします。専用のウィンドウで開き、オフラインでも使えます",
|
||||
"install.dock_1": "「ファイル」›「Dockに追加」を選びます。",
|
||||
"install.dock_2": "「追加」をクリックします。",
|
||||
@@ -1828,7 +1867,18 @@
|
||||
"issue.note_save_failed": "スクリーンショットを保存できませんでした: {error}",
|
||||
"issue.note_saved": "スクリーンショットを保存しました。GitHubの下書きにPNGを添付してください。",
|
||||
"issue.save_screenshot": "スクリーンショットを保存…",
|
||||
"joint.find_limits": "制限を検出",
|
||||
"joint.find_limits_failed": "制限の検出がエラーで停止しました",
|
||||
"joint.find_limits_hint": "このジョイントが動かす部品が他の部品に最初に触れる位置から制限を入力します。現在の位置から両方向に回転またはスライドして調べます。コピー上で行うため、OK を押すまで何も変わりません。",
|
||||
"joint.find_limits_stale": "制限を検出した後にジョイントまたは設計が変更されました。もう一度検出してください",
|
||||
"joint.finding_limits": "制限を検出中…",
|
||||
"joint.from_bodies_hint": "選択した各ボディを、位置はそのままで専用の新しいコンポーネントに移動します。",
|
||||
"joint.limit_contact": "{side} {value}: {a} が {b} に接触",
|
||||
"joint.limit_limit": "{side} {value}: 別の値またはジョイントの制限",
|
||||
"joint.limit_loop": "{side} {value}: これを超えると閉ループが閉じません",
|
||||
"joint.limit_range": "{side}: {range} 以内に接触なし、そのままにしました",
|
||||
"joint.limit_turn": "{side} {value}: 1 回転以内に接触なし",
|
||||
"joint.limits_overlapping": "この位置ですでに重なっています: {pairs}。さらに深く入り込んだ場合にのみ検出が止まります。",
|
||||
"joint.one_body": "両方の選択が {body} 上にあります。2 つ目は別の部品で選択してください。",
|
||||
"joint.one_component": "これらの部品は 1 つのコンポーネントのボディです: {bodies}。ジョイントはコンポーネント同士を動かすため、部品ごとに専用のコンポーネントが必要です。",
|
||||
"joint.rigid_group_one_component": "これらの {count} 個のボディは、すべて 1 つのコンポーネント内(またはデザイン自体)にあります。先に「{command}」を使用してください。",
|
||||
@@ -2091,12 +2141,19 @@
|
||||
"parts.family.button_head_cap_screw": "ボタンヘッド六角穴付きボルト",
|
||||
"parts.family.dowel_pin": "平行ピン",
|
||||
"parts.family.hex_bolt": "六角ボルト",
|
||||
"parts.family.hex_cap_screw_inch": "六角ボルト(インチ)",
|
||||
"parts.family.hex_nut": "六角ナット",
|
||||
"parts.family.hex_nut_inch": "六角ナット(インチ)",
|
||||
"parts.family.set_screw": "六角穴付き止めねじ 平先",
|
||||
"parts.family.socket_head_cap_screw": "六角穴付きボルト",
|
||||
"parts.family.washer": "平座金",
|
||||
"parts.family.washer_sae": "平座金、SAE(小形)",
|
||||
"parts.family.washer_uss": "平座金、USS(大形)",
|
||||
"parts.holes": "穴 (省略可)",
|
||||
"parts.placement_hint": "1 つ以上の穴を選ぶとそれぞれに部品を取り付けます。選ばずに OK を押すと原点に配置します",
|
||||
"parts.system": "単位系",
|
||||
"parts.system.inch": "インチ",
|
||||
"parts.system.metric": "メートル",
|
||||
"pattern.alignment_2": "配置 2",
|
||||
"pattern.alignment_hint": "個数には元の図形が含まれます。中央配置ではコピーを両側に配置し、反転で偶数個の場合に多く配置する側を切り替えます。",
|
||||
"pattern.direction_1": "方向 1",
|
||||
@@ -2135,6 +2192,18 @@
|
||||
"prefs.alt_frees_manipulators": "Alt / Option でマニピュレーターの矢印も自由に動かす",
|
||||
"prefs.anti_aliasing_amd_dx12": "起動時のチェックでマルチサンプリングでは何も描画されず、1×では正常に描画されたため、アンチエイリアシングを1×に設定しました。上で別の設定を選択できます。再起動後のチェックに合格すると、その設定が適用されます。",
|
||||
"prefs.anti_aliasing_note": "輪郭のぎざぎざを滑らかにします。",
|
||||
"prefs.appearance.colour_vision_help": "意味を持つ色を変更します:スケッチ状態、軸、曲げ、解析、アイコン。",
|
||||
"prefs.appearance.desktop_only": "デスクトップのみ",
|
||||
"prefs.appearance.follow_help": "別のツールがファイルを書き換えると再読み込みします。",
|
||||
"prefs.appearance.interface_scale": "インターフェースの倍率",
|
||||
"prefs.appearance.next_themes": "次のテーマ",
|
||||
"prefs.appearance.option_count": "{count} 個のオプション",
|
||||
"prefs.appearance.option_count_one": "{count} 個のオプション",
|
||||
"prefs.appearance.previous_themes": "前のテーマ",
|
||||
"prefs.appearance.theme_count": "{count} 個のテーマ",
|
||||
"prefs.appearance.theme_own": "テーマ独自",
|
||||
"prefs.appearance.viewport_colours": "3D ビューの色",
|
||||
"prefs.appearance.viewport_logo": "ビューポートとロゴ",
|
||||
"prefs.applies_on_restart": "再起動後に適用",
|
||||
"prefs.assistant": "アシスタント",
|
||||
"prefs.auto_operation": "操作を自動で選択",
|
||||
@@ -2149,6 +2218,8 @@
|
||||
"prefs.bottom_centre": "下中央",
|
||||
"prefs.bottom_right": "右下",
|
||||
"prefs.browser": "ブラウザー",
|
||||
"prefs.browser_components_open": "コンポーネントを最初から展開",
|
||||
"prefs.browser_components_open_note": "ブラウザーのすべてのコンポーネントを以前と同じく最初から展開して表示します。オフの場合は展開するまで折りたたまれています。",
|
||||
"prefs.browser_eye_left": "表示切り替えを折りたたみ矢印の隣に表示",
|
||||
"prefs.browser_eye_left_note": "ブラウザーの各行の表示切り替えを、行の右端の列ではなく、以前のように折りたたみ矢印の隣に表示します。",
|
||||
"prefs.browser_reveal_on_hover": "ホバーで表示",
|
||||
@@ -2198,7 +2269,19 @@
|
||||
"prefs.font_sample": "いろはにほへと · Ø12.50 mm · 45.0° · 0123456789",
|
||||
"prefs.font_unreadable": "選択したフォントを読み込めませんでした。",
|
||||
"prefs.general": "一般",
|
||||
"prefs.general_note": "言語、保存、更新。",
|
||||
"prefs.general.active": "使用中",
|
||||
"prefs.general.crash_reports": "クラッシュレポート",
|
||||
"prefs.general.edited": "変更あり",
|
||||
"prefs.general.held": "保存対象",
|
||||
"prefs.general.import": "インポート...",
|
||||
"prefs.general.last_check": "最終確認: {time}",
|
||||
"prefs.general.minutes": "分",
|
||||
"prefs.general.name_setup": "この設定に名前を付ける",
|
||||
"prefs.general.profile_name": "プロファイル名",
|
||||
"prefs.general.save_new": "新規保存",
|
||||
"prefs.general.step_import": "STEP とインポート",
|
||||
"prefs.general.this_computer": "このコンピューター",
|
||||
"prefs.general_note": "プロファイル、言語、ファイル、更新。",
|
||||
"prefs.gesture_orbit": "回転",
|
||||
"prefs.gesture_pan": "移動",
|
||||
"prefs.gesture_zoom": "ズーム",
|
||||
@@ -2255,10 +2338,10 @@
|
||||
"prefs.logo_accent": "アクセント",
|
||||
"prefs.logo_theme": "テーマの色",
|
||||
"prefs.max_file_size": "開けるファイルの最大サイズ",
|
||||
"prefs.max_file_size_design": "デザインファイル",
|
||||
"prefs.max_file_size_design": "開くファイルの上限: デザイン",
|
||||
"prefs.max_file_size_flag": "今回は --max-file-size {size} を指定して起動したため、SolidCraft を閉じるまでその値が適用されます。この設定は、そのオプションなしで起動したときに適用されます。",
|
||||
"prefs.max_file_size_note": "既定値は 512 MB と 256 MB、上限は 16 GB です。ファイルを開くには、サイズの数倍のメモリが必要です。大きなインポートを保持するデザインの保存と再読み込みには、デザインファイルの上限を上げる必要がある場合があります。",
|
||||
"prefs.max_file_size_step": "STEP・インポートファイル",
|
||||
"prefs.max_file_size_step": "開くファイルの上限: STEP・インポート",
|
||||
"prefs.max_file_size_web": "ウェブ版で開けるのは STEP・インポートファイルが 512 MB、デザインが 256 MB までです。ブラウザーのタブはそれ以上のファイルを保持できません。デスクトップ版では上限を上げられます。",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "大きなアイコン",
|
||||
@@ -2275,7 +2358,7 @@
|
||||
"prefs.navigator_fit_note": "キューブの面・角・矢印やギズモの軸をクリックすると、どの方向からでも全体が収まる一定の距離(外接球)からモデルの中心を見ます。オンの間はホームビューも同じズームと中心を使うため、その後に選んだ視点は向きだけが変わります。オフ: 視点はその場で回転し、注視点とズームを保ちます。",
|
||||
"prefs.network_host_remove": "削除",
|
||||
"prefs.network_locations": "デザイン内のネットワーク上の場所を許可",
|
||||
"prefs.network_locations_note": "オフ: 別のコンピューター (\\\\server\\share) にあるデザインのリンク先ファイルは、ここに表示されたサーバー、または確認時に許可したサーバーからのみ開きます。",
|
||||
"prefs.network_locations_note": "ファイルサーバーでデザインを管理する組織向け。オフの場合は許可したサーバーのみ。",
|
||||
"prefs.network_locations_section": "ネットワーク上の場所",
|
||||
"prefs.not_on_gpu": "この GPU は非対応",
|
||||
"prefs.on": "オン",
|
||||
@@ -2301,10 +2384,12 @@
|
||||
"prefs.profile_applied": "プロファイルを適用しました。",
|
||||
"prefs.profile_changed": "プロファイルの保存後に設定が変更されています。「保存」でプロファイルに反映されます。",
|
||||
"prefs.profile_deleted": "プロファイルを削除しました。",
|
||||
"prefs.profile_export": "エクスポート…",
|
||||
"prefs.profile_export": "エクスポート",
|
||||
"prefs.profile_exported": "プロファイルをエクスポートしました。",
|
||||
"prefs.profile_imported": "プロファイルをインポートしました。切り替えるには「使用中のプロファイル」で選択してください。",
|
||||
"prefs.profile_imported": "プロファイルをインポートしました。",
|
||||
"prefs.profile_name_hint": "プロファイル名(例: ノートPC)",
|
||||
"prefs.profile_operations": "処理をプロファイル",
|
||||
"prefs.profile_operations_note": "SMLib の呼び出し、カーネル処理、フィーチャー、コマンド、インポートにかかる時間を記録します。エージェントは diagnostics.profile で読み取ります。",
|
||||
"prefs.profile_rename": "名前を変更",
|
||||
"prefs.profile_renamed": "プロファイルの名前を変更しました。",
|
||||
"prefs.profile_save": "保存",
|
||||
@@ -2323,16 +2408,20 @@
|
||||
"prefs.render_previous_geometry_budget": "レンダープレビューで以前のジオメトリ予算を使用",
|
||||
"prefs.render_previous_geometry_budget_help": "デバイスに基づく予算の代わりに、以前の固定ジオメトリ上限(64ビットデスクトップでは512 MiB、その他では128 MiB)に戻します。",
|
||||
"prefs.reports_saved": "{n} 件保存済み",
|
||||
"prefs.restore_page_defaults": "このページを既定値に戻す",
|
||||
"prefs.ribbon_click_ends_tool": "実行中のツールのボタンをクリックすると終了する",
|
||||
"prefs.ribbon_click_ends_tool_note": "使用中のスケッチツールの点灯したリボンボタンをクリックするとツールが終了し、ビューのクリックで再び選択できます。オフ: クリックするとツールが最初から開始されます。",
|
||||
"prefs.ribbon_reveal": "検索結果をリボンに表示",
|
||||
"prefs.ribbon_reveal_note": "コマンド検索で結果にポインターを合わせると、そのコマンドがリボンのどこにあるか(タブ、ボタン、またはパネルのドロップダウン内の行)を表示します。検索から開始したコマンドはそこで点滅し、ドロップダウンから実行中のコマンドはそのパネルに印が付きます。",
|
||||
"prefs.right_click": "右クリック",
|
||||
"prefs.saving": "自動保存と復元",
|
||||
"prefs.saving": "起動と保存",
|
||||
"prefs.scale_note": "ウィンドウ内では Ctrl + / Ctrl − でも変更できます。100% のボタンは 28 px です。",
|
||||
"prefs.scale_text": "拡大率と文字",
|
||||
"prefs.search": "設定を検索",
|
||||
"prefs.search_empty": "一致する設定がありません",
|
||||
"prefs.section_hatch": "断面ハッチング",
|
||||
"prefs.section_hatch_by_part": "部品ごと",
|
||||
"prefs.section_hatch_one": "単一スタイル",
|
||||
"prefs.select": "選択",
|
||||
"prefs.select_one_item": "項目を 1 つ選択",
|
||||
"prefs.select_other_hidden": "「他を選択」で非表示のジオメトリを選択",
|
||||
@@ -2447,6 +2536,12 @@
|
||||
"prefs.under_cursor": "カーソル下",
|
||||
"prefs.units_precision": "単位と精度",
|
||||
"prefs.updates": "更新",
|
||||
"prefs.validation": "ボディのチェック",
|
||||
"prefs.validation.each_feature": "フィーチャーごとに 1 回",
|
||||
"prefs.validation.each_step": "各ステップの後",
|
||||
"prefs.validation.label": "カーネルの完全チェック",
|
||||
"prefs.validation.off": "オフ (試験的)",
|
||||
"prefs.validation_note": "各ステップの後: スイープ、ブーリアン、ブレンドのたびにチェックします。\nフィーチャーごとに 1 回: 各フィーチャーの結果を 1 回チェックします。\nオフ (試験的): チェックしません。",
|
||||
"prefs.value_boxes_in_view": "ビューでハンドルの横に値を表示",
|
||||
"prefs.value_boxes_in_view_tip": "コマンドの値(距離、半径など)をモデル上のハンドルの横のボックスに表示します。オフにすると、値はコマンドウィンドウに入力します。選択後はそのウィンドウにキーボード入力が入り、モデルを覆うものはありません。",
|
||||
"prefs.view": "ビューの動作",
|
||||
@@ -2481,6 +2576,17 @@
|
||||
"recovery.just_now": "たった今",
|
||||
"recovery.min_ago": "{count}分前",
|
||||
"recovery.never_saved": "未保存",
|
||||
"refused.change": "{body} を変更できませんでした。",
|
||||
"refused.cut": "{body} をカットできませんでした。",
|
||||
"refused.face_curved": "曲面の 1 つがモデリングに使えません。",
|
||||
"refused.face_flat": "平らな面の 1 つがモデリングに使えません。",
|
||||
"refused.face_freeform": "自由曲面の 1 つがモデリングに使えません。",
|
||||
"refused.face_rounded": "丸みのある面の 1 つがモデリングに使えません。",
|
||||
"refused.faces_apart": "面どうしが十分に合っておらず、モデリングに使えません。",
|
||||
"refused.intersect": "{body} と交差できませんでした。",
|
||||
"refused.join": "{body} に結合できませんでした。",
|
||||
"refused.repair": "パーツを修復",
|
||||
"refused.show_face": "面を表示",
|
||||
"render.adaptive_absolute": "RGB 絶対誤差の下限",
|
||||
"render.adaptive_coverage": "被覆率の誤差目標",
|
||||
"render.adaptive_enable": "独立した予備サンプルで配分",
|
||||
@@ -3948,11 +4054,20 @@
|
||||
"the section's profiles must be in one sketch": "セクションのプロファイルは1つのスケッチ内にある必要があります",
|
||||
"the two origins are on the same component": "2つの原点が同じコンポーネント上にあります",
|
||||
"thread.automatic_size": "サイズを自動選択",
|
||||
"thread.chamfer_depth_needed": "この形状では必須",
|
||||
"thread.designation": "ねじの呼び",
|
||||
"thread.direction": "ねじの方向",
|
||||
"thread.drill_range": "タップメーカーの下穴ドリルは {drill} mm です。めねじの規格の内径は {minor} mm です。{low}~{high} mm のドリルを使用できます。",
|
||||
"thread.drill_range_custom": "下穴ドリルは外径からピッチを引いた {drill} mm です。めねじの規格の内径は {minor} mm です。{low}~{high} mm のドリルを使用できます。",
|
||||
"thread.drill_range_taper": "タップメーカーの下穴ドリルは {drill} mm です。ねじ終端での規格の内径は {minor} mm です。{low}~{high} mm のドリルを使用できます。",
|
||||
"thread.end_square": "直角",
|
||||
"thread.fade_short": "1.25 P(ISO 3508)",
|
||||
"thread.fade_standard": "2.5 P(ISO 3508)",
|
||||
"thread.fits_the_face": "面に合うサイズ。例: {example}",
|
||||
"thread.form_bspp": "管用平行ねじ G(ISO 228-1)",
|
||||
"thread.form_bspt": "管用テーパねじ R、Rc(ISO 7-1)",
|
||||
"thread.form_npt": "管用テーパねじ NPT(ASME B1.20.1)",
|
||||
"thread.form_rp": "管用平行めねじ Rp(ISO 7-1)",
|
||||
"thread.iso_metric": "ISOメートルねじ",
|
||||
"thread.left_hand": "左ねじ",
|
||||
"thread.right_hand": "右ねじ",
|
||||
"thread.select_size": "サイズを選択",
|
||||
"timeline.delete_after_marker_one": "履歴マーカー以降の 1 個のフィーチャーを削除",
|
||||
"timeline.delete_after_marker_other": "履歴マーカー以降の {count} 個のフィーチャーを削除",
|
||||
@@ -4126,6 +4241,7 @@
|
||||
"ui.agent_chat_auth_terminal": "ターミナルでこれを実行してから、もう一度「接続」を押してください:",
|
||||
"ui.agent_chat_auth_title": "エージェントにサインイン",
|
||||
"ui.agent_chat_cancel": "キャンセル",
|
||||
"ui.agent_chat_cancel_timeout": "エージェントが停止要求に応答しなかったため、接続を切断しました。続行するには再接続してください。",
|
||||
"ui.agent_chat_cannot_start": "インストールされていますが、起動しません: {error}",
|
||||
"ui.agent_chat_changes": "{n} 件の変更(元に戻すは1回)",
|
||||
"ui.agent_chat_changes_n": "{n} 件の変更",
|
||||
@@ -4203,6 +4319,7 @@
|
||||
"ui.agent_chat_no_images": "このエージェントは画像を受け付けません",
|
||||
"ui.agent_chat_no_picture": "クリップボードに画像がありません",
|
||||
"ui.agent_chat_no_tool_call": "モデルが SolidCraft のツールを使わなかったため、デザインは変更されませんでした。ツール呼び出しに対応したモデルを選んでください。エージェントチャットのガイドの「ローカルモデル」を参照してください。",
|
||||
"ui.agent_chat_node_too_old": "Node.js {version} はこのエージェントには古すぎます。Node.js 20.10 以降をインストールしてから、更新を押してください。",
|
||||
"ui.agent_chat_none_found": "コーディングエージェントが見つかりません。次のいずれかをインストールしてから「再検索」を押してください:",
|
||||
"ui.agent_chat_none_found_short": "エージェントが見つかりません",
|
||||
"ui.agent_chat_not_installed": "未インストール",
|
||||
@@ -4422,6 +4539,8 @@
|
||||
"ui.centerline": "中心線",
|
||||
"ui.chain": "チェーン",
|
||||
"ui.chamfer": "面取り",
|
||||
"ui.chamfer_angle": "面取り角度",
|
||||
"ui.chamfer_depth": "面取り深さ",
|
||||
"ui.chamfer_type": "面取りタイプ",
|
||||
"ui.change_workspace": "ワークスペースの変更",
|
||||
"ui.circle": "円",
|
||||
@@ -4452,10 +4571,16 @@
|
||||
"ui.colour_vision_sample": "サンプル",
|
||||
"ui.colour_vision_tritan": "3型色覚(T型)",
|
||||
"ui.colour_vision_tritan_tip": "青を感じにくい、または感じない場合に",
|
||||
"ui.comb_length": "コームの長さ",
|
||||
"ui.comb_length_tip": "歯の長さ: 自動の長さの倍率です。自動の長さでは、いちばん長い歯が表示中の曲線の大きさの 5 分の 1 になります。",
|
||||
"ui.comb_teeth": "歯の数",
|
||||
"ui.comb_teeth_tip": "各コームの歯の数です。曲線に沿って等間隔に並びます。歯を増やすと、曲率の変わり方がより細かく見えます。",
|
||||
"ui.combine": "結合",
|
||||
"ui.coming_later": "準備中",
|
||||
"ui.command_done": "{command} 完了",
|
||||
"ui.command_palette_hint": "コマンドを入力(例: {example})— または S キーで検索",
|
||||
"ui.components_from_bodies_hint": "選択した各ボディと同じ名前のコンポーネントを、アクティブなコンポーネントの下に作成します。",
|
||||
"ui.components_from_bodies_required": "ボディを1つ以上選択してください。",
|
||||
"ui.concentric": "同心",
|
||||
"ui.cone": "円錐",
|
||||
"ui.conic_curve": "円錐曲線",
|
||||
@@ -4474,11 +4599,24 @@
|
||||
"ui.construction.ribbon_label": "補助線",
|
||||
"ui.construction.side_hint": "補助直線: 線を伸ばす側をクリック。右クリックで最初から",
|
||||
"ui.context_menu": "コンテキスト メニュー",
|
||||
"ui.continuity_crease": "折れ",
|
||||
"ui.continuity_curvature": "曲率",
|
||||
"ui.continuity_curvature_change": "曲率変化",
|
||||
"ui.continuity_curvature_change_tip": "曲率変化 (G3): 境目で曲率の変化もなめらかです。",
|
||||
"ui.continuity_curvature_tip": "曲率連続 (G2): 映り込みが折れずに通ります。",
|
||||
"ui.continuity_g0": "G0 折れ",
|
||||
"ui.continuity_g1": "G1 接線",
|
||||
"ui.continuity_g2": "G2 曲率",
|
||||
"ui.continuity_gap": "隙間",
|
||||
"ui.continuity_position": "位置",
|
||||
"ui.continuity_position_tip": "位置 (G0): 面はつながりますが、境目に折れがあります。",
|
||||
"ui.continuity_smooth_curvature_change": "なめらかな曲率変化",
|
||||
"ui.continuity_smooth_curvature_change_tip": "なめらかな曲率変化 (G4): 境目で曲率の変わり方そのものもなめらかに続きます。",
|
||||
"ui.continuity_tangent": "接線",
|
||||
"ui.continuity_tangent_tip": "接線 (G1): 折れはありませんが、境目で曲率が飛びます。",
|
||||
"ui.copy": "コピー",
|
||||
"ui.copy_details": "詳細をコピー",
|
||||
"ui.copy_details_tip": "調べたり報告したりできるよう、メッセージ全文をコピーします。",
|
||||
"ui.corner": "コーナー",
|
||||
"ui.corners": "コーナー",
|
||||
"ui.cosmetic_thread_markings": "ねじの簡略表示",
|
||||
@@ -4596,8 +4734,10 @@
|
||||
"ui.edit_sketch_empty": "編集できるスケッチはありません",
|
||||
"ui.ellipse": "楕円",
|
||||
"ui.emboss_wrap_note": "サポートされる円柱に接する直交線のプロファイルは自動的に巻き付けられます。対象を選ぶには壁を選択してください。",
|
||||
"ui.end": "終端",
|
||||
"ui.end_radius": "終了半径",
|
||||
"ui.end_scale": "終端スケール",
|
||||
"ui.ends": "端部",
|
||||
"ui.entities": "エンティティ",
|
||||
"ui.equal": "等しい",
|
||||
"ui.equation_end": "終了",
|
||||
@@ -4617,6 +4757,7 @@
|
||||
"ui.export_ellipsis_keymap": "エクスポート…",
|
||||
"ui.export_keymap_tip": "ショートカット、Sボックスのお気に入り、ツールバー設定をファイルに保存",
|
||||
"ui.extend": "延長",
|
||||
"ui.extent": "範囲",
|
||||
"ui.extent_type": "範囲タイプ",
|
||||
"ui.extract_component": "ファイルに抽出…",
|
||||
"ui.extract_design_changed": "ファイルの選択中にデザインが変更されました。ファイルへの抽出をやり直してください。",
|
||||
@@ -4625,11 +4766,39 @@
|
||||
"ui.extrude_auto_to_object": "選択した対象まで押し出し",
|
||||
"ui.extrude_auto_to_object_tip": "押し出しでプロファイルを選択した後、その平面外の面・頂点・平行な平面を選ぶと、範囲タイプが「オブジェクトまで」に切り替わります",
|
||||
"ui.factor": "倍率",
|
||||
"ui.fade": "切り上げ",
|
||||
"ui.fade_length": "切り上げ長さ",
|
||||
"ui.feature_history": "フィーチャー履歴",
|
||||
"ui.feature_options": "フィーチャーオプション",
|
||||
"ui.features": "フィーチャー",
|
||||
"ui.file": "ファイル",
|
||||
"ui.fillet": "フィレット",
|
||||
"ui.fillet_corner_ball": "ボール",
|
||||
"ui.fillet_corner_ball_tip": "転がるボールの球面の一部で、各フィレットに接します (G1)。",
|
||||
"ui.fillet_corner_curvature_tip": "曲率 (G2): なめらかなパッチ。コーナーを各エッジに沿ってセットバック分 (入力しなければ半径の {factor} 倍) 後退させてパッチで埋め、映り込みが折れずに通ります。",
|
||||
"ui.fillet_corner_needs_constant": "スムーズパッチのコーナー (G2) には、一定の半径と厳密な半径フィットが必要です。",
|
||||
"ui.fillet_corner_patch": "スムーズパッチ",
|
||||
"ui.fillet_corner_patch_tip": "曲率連続 (G2): コーナーを各エッジに沿って後退させてパッチで埋め、映り込みが折れずに通ります。",
|
||||
"ui.fillet_corner_tangent_tip": "接線 (G1): 転がるボールのコーナー。転がるボールの球面の一部で、各フィレットに接します。",
|
||||
"ui.fillet_error_corner": "ここではなめらかなコーナーを作れませんでした。別のセットバックか、接線のコーナーを試してください。",
|
||||
"ui.fillet_error_failed": "このフィレットをこれらのエッジに作れませんでした。別の半径を試すか、一度に丸めるエッジを減らしてください。",
|
||||
"ui.fillet_error_setback_short": "このコーナーのパッチにはセットバックが短すぎます。{least} 以上で作れます。より長いセットバックを試してください。",
|
||||
"ui.fillet_error_too_large": "これらのエッジの隣の面に対して半径が大きすぎます。小さい半径を試すか、「面の境界」を「最大半径」にしてください。",
|
||||
"ui.fillet_fit": "面の境界",
|
||||
"ui.fillet_fit_exact": "正確な半径",
|
||||
"ui.fillet_fit_needs_constant": "半径フィットには一定の半径が必要です。",
|
||||
"ui.fillet_fit_reduce": "最大半径",
|
||||
"ui.fillet_fit_taper": "狭めた半径",
|
||||
"ui.fillet_fit_tip": "半径がエッジの隣の面で収まる大きさを超えるとき。\n正確な半径: 指定どおりの半径。収まらなければ丸めずに理由を知らせます。\n最大半径: 各エッジを収まる最大の半径で丸めます。\n狭めた半径: 半径を保ち、面が足りない所だけフィレットを細くします。",
|
||||
"ui.fillet_options_tip": "正接チェーン: 選んだエッジからなめらかに続くエッジも丸めます。\nプレビュー: 編集中に結果をビューに表示します。",
|
||||
"ui.fillet_setback": "セットバック",
|
||||
"ui.fillet_setback_hint": "{factor} × 半径",
|
||||
"ui.fillet_setback_least": "{least} より大きく",
|
||||
"ui.fillet_setback_tip": "なめらかなコーナーパッチが始まる位置: コーナーから各エッジに沿った距離。既定値は半径の {factor} 倍。最小セットバック (セットバックなしでラウンド同士が交わる位置。面どうしが直角なら半径) より大きくする必要があります。",
|
||||
"ui.fillet_smooth_radius": "半径をなめらかに変化",
|
||||
"ui.fillet_type_choices_tip": "一定の半径: エッジ全体で一つの半径。\n弦長: 面がどんな角度で交わっても、フィレットの幅が同じ。\n可変半径: 両端の半径と、追加すればエッジ沿いの半径。\n曲率連続: 平面と接する所で映り込みが折れない断面 (G2)。\nスプライン: エッジ沿いの半径が、設定した制御半径に従います。",
|
||||
"ui.fillet_type_curvature": "曲率連続",
|
||||
"ui.fillet_type_tip": "エッジを横切る断面。曲率連続: 平面の間の曲率連続 (G2) 断面で、映り込みが面からフィレットへ折れずにつながります。",
|
||||
"ui.filter_commands_or_keys": "コマンドまたはキーを検索",
|
||||
"ui.findInTimeline": "タイムラインで検索",
|
||||
"ui.findInWindow": "ウィンドウで検索",
|
||||
@@ -4761,6 +4930,7 @@
|
||||
"ui.history_sketch_listed_under": "{feature} の下に表示",
|
||||
"ui.history_title": "履歴",
|
||||
"ui.history_toggle_names": "フィーチャー名を表示・非表示",
|
||||
"ui.history_waits_for_editor": "元に戻す、やり直す、履歴は、フィーチャーエディターが開くまで待機します",
|
||||
"ui.hole": "穴",
|
||||
"ui.hole_tap_type": "ねじ穴タイプ",
|
||||
"ui.hole_type": "穴タイプ",
|
||||
@@ -4778,6 +4948,7 @@
|
||||
"ui.inspect": "検査",
|
||||
"ui.instances": "インスタンス",
|
||||
"ui.intersect": "交差",
|
||||
"ui.iso_4753": "ISO 4753",
|
||||
"ui.isolate": "分離表示",
|
||||
"ui.join": "結合",
|
||||
"ui.joint_play": "▶ 再生",
|
||||
@@ -4880,6 +5051,7 @@
|
||||
"ui.part_size_needs_a_part": "サイズを変更する挿入済みの標準パーツ(ねじ、ナット、座金、ピン)を選択してください。ボディを拡大・縮小するには尺度を使います。",
|
||||
"ui.part_size_shared": "パーツを再構築し、追加したフィーチャを置き換えます。このコンポーネントのすべての配置が更新されます。",
|
||||
"ui.paste": "貼り付け",
|
||||
"ui.patch_continuity_corner_tip": "パッチは各辺に沿って、周囲の面と選んだ連続性でつながります。周囲の面どうしが異なる曲率で接するコーナーでは、そこで接線 (G1) にとどまります。",
|
||||
"ui.path": "パス",
|
||||
"ui.pattern_object_type_tip": "ボディ: ビューまたはブラウザで選ぶ立体形状。それぞれがまるごとコピーされます。\nフィーチャー: 押し出しや穴など履歴のステップ。履歴で、またはそれが作った面で選びます。そのステップが繰り返されます。\nスケッチはどちらでもありません: 曲線はスケッチ内のパターンかミラーで繰り返すか、先に押し出してください。",
|
||||
"ui.pattern_on_path": "パス上のパターン",
|
||||
@@ -5056,6 +5228,8 @@
|
||||
"ui.sketch_intersect_geometry": "ジオメトリ",
|
||||
"ui.sketch_intersect_projection_link": "投影リンク",
|
||||
"ui.sketch_intersect_selection_filter": "選択フィルター",
|
||||
"ui.sketch_keys_only": "スケッチ中はモデルのキーを使わない",
|
||||
"ui.sketch_keys_only_tip": "スケッチの編集中は、スケッチのキーとどこでも使えるキーだけが働きます。E や F などのモデルのキーは何もしないので、うっかり押したキーで重いスケッチが閉じることはありません。",
|
||||
"ui.sketch_modifiers": "スケッチ修飾キー",
|
||||
"ui.sketch_project_geometry": "ジオメトリ",
|
||||
"ui.sketch_report.all_constrained": "すべて完全に拘束されています。",
|
||||
@@ -5073,7 +5247,7 @@
|
||||
"ui.slicer_no_response": "{slicer} が時間内に応答しませんでした。ファイルは {path} に保存済みです。スライサーが起動したら、もう一度送ってください。",
|
||||
"ui.slicer_resend_hint": "もう一度送ると、スライサーで開いているファイルが置き換わります。",
|
||||
"ui.slicer_single_instance": "すでに起動しているスライサーでファイルを開く",
|
||||
"ui.slicer_single_instance_tip": "PrusaSlicer、SuperSlicer、Cura は、別のウィンドウを起動せず、開いているウィンドウでファイルを読み込みます。ほかのスライサーはそのまま起動します。PrusaSlicer 3.0 は常にこの動作です。ランチャーはほかの方法ではファイルを受け取れません。",
|
||||
"ui.slicer_single_instance_tip": "実行中のウィンドウにファイルを読み込むスライサー用。",
|
||||
"ui.slicer_starting": "{slicer} を起動しています…",
|
||||
"ui.slicer_still_starting": "まだ {slicer} の起動を待っています。ほかには何も送信していません。",
|
||||
"ui.slicer_stl_fallback": "3MF を書き込めなかったため、STL で送信しました。",
|
||||
@@ -5218,13 +5392,13 @@
|
||||
"update.available": "SolidCraft {version} が利用できます",
|
||||
"update.button": "アップデート",
|
||||
"update.button_version": "{version} にアップデート",
|
||||
"update.check_automatically": "アップデートを自動的に確認",
|
||||
"update.check_automatically": "起動時と1日1回確認",
|
||||
"update.check_for_updates": "アップデートを確認",
|
||||
"update.check_now": "今すぐ確認",
|
||||
"update.check_unavailable": "このインストールではアップデートを確認できません。",
|
||||
"update.checking": "確認中…",
|
||||
"update.copy": "コピー",
|
||||
"update.dmg_opened": "ディスクイメージを検証して開きました: SolidCraft をアプリケーションフォルダにドラッグして古いものを置き換え、SolidCraft を再起動してください。",
|
||||
"update.dmg_opened": "ディスクイメージを検証して開きました: SolidCraft を終了し、新しい SolidCraft をアプリケーションフォルダにドラッグして古いものを置き換えてから、SolidCraft をもう一度開いてください。",
|
||||
"update.download": "ダウンロード",
|
||||
"update.download_open": "ダウンロードして開く",
|
||||
"update.downloading": "ダウンロードと検証中… {done} / {total} MB",
|
||||
|
||||
+155
-6
@@ -407,6 +407,7 @@
|
||||
"Recover unsaved design?": "저장되지 않은 디자인을 복구할까요?",
|
||||
"Rectangular": "직사각형",
|
||||
"Reference face": "참조 면",
|
||||
"Reference plane": "참조 평면",
|
||||
"Relief Depth": "릴리프 깊이",
|
||||
"Relief Width": "릴리프 너비",
|
||||
"Remove Tools": "공구 제거",
|
||||
@@ -422,6 +423,8 @@
|
||||
"Rename Feature": "피처 이름 바꾸기",
|
||||
"Rename Folder": "폴더 이름 바꾸기",
|
||||
"Render Stage": "렌더링 단계",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "지난번에 열려 있던 디자인 다시 열기",
|
||||
"Repair Part": "부품 복구",
|
||||
"Replace existing library file": "기존 라이브러리 파일 바꾸기",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "버그 신고 — 스크린샷을 검토하고 GitHub 초안 열기",
|
||||
"Report text copied. Paste it into GitHub and submit your report there.": "보고서 텍스트를 복사했습니다. GitHub에 붙여넣고 그곳에서 보고서를 제출하세요.",
|
||||
@@ -680,6 +683,7 @@
|
||||
"browser.edit_canvas_title": "캔버스 편집: {name}",
|
||||
"browser.edit_point_cloud_title": "포인트 클라우드 편집: {name}",
|
||||
"browser.move_title": "이동: {name}",
|
||||
"browser.needs_repair": "{name}: 복구 필요",
|
||||
"browser.parameters": "매개변수 ({count})",
|
||||
"browser.precision": "정밀도: {precision}",
|
||||
"browser.sketch_attachment_lost": "{name} (부착 해제됨)",
|
||||
@@ -715,6 +719,7 @@
|
||||
"cmd.assembly_mass_properties": "질량 특성",
|
||||
"cmd.body.print_face": "출력 바닥면",
|
||||
"cmd.body_rename": "바디 이름 변경",
|
||||
"cmd.body_repair": "부품 복구",
|
||||
"cmd.browser_group": "새 그룹",
|
||||
"cmd.browser_move": "브라우저 항목 이동",
|
||||
"cmd.browser_order": "브라우저 항목 정렬",
|
||||
@@ -788,6 +793,8 @@
|
||||
"cmd.diagnostics_crash_reports": "충돌 보고서",
|
||||
"cmd.diagnostics_crash_reports_clear": "충돌 보고서 삭제",
|
||||
"cmd.diagnostics_crash_test": "충돌 테스트",
|
||||
"cmd.diagnostics_profile": "프로파일",
|
||||
"cmd.diagnostics_validation": "검증",
|
||||
"cmd.doc_recover": "설계 복구",
|
||||
"cmd.doc_recovery_discard": "복구된 설계 폐기",
|
||||
"cmd.doc_recovery_list": "복구 가능한 설계",
|
||||
@@ -1162,6 +1169,7 @@
|
||||
"crash.report": "보고…",
|
||||
"crash.unclean_explanation": "이후 실행 시 감지되었으며 정확한 종료 시각은 기록되지 않았습니다. 시스템 종료나 강제 중지로도 비정상 종료가 발생할 수 있습니다.",
|
||||
"delete.and_more": "… 외 {count}개",
|
||||
"diagnostic.reselect_reference": "{feature}: 참조 `{reference}`이(가) 선택했을 때의 모습으로 더 이상 존재하지 않습니다. 다시 선택하세요",
|
||||
"dialog.bodies_count": "바디 {count}개",
|
||||
"dialog.clearance_invalid": "간격은 0 이상의 길이여야 합니다",
|
||||
"dialog.compute": "계산",
|
||||
@@ -1212,6 +1220,14 @@
|
||||
"file_load.reading_percent": "파일 읽는 중... {percent}%",
|
||||
"file_load.rebuilding": "피처 다시 빌드 중 {done}/{total}",
|
||||
"file_load.recovering": "디자인 복구 중",
|
||||
"file_load.step_build": "모델 만드는 중",
|
||||
"file_load.step_feature": "피처 {done}/{total}",
|
||||
"file_load.step_part": "부품 {done}/{total}",
|
||||
"file_load.step_percent": "{percent}%",
|
||||
"file_load.step_read": "파일 읽는 중",
|
||||
"file_load.step_rebuild": "피처 다시 빌드 중",
|
||||
"file_load.step_repair": "부품 확인 및 복구 중",
|
||||
"file_load.step_show": "뷰 준비 중",
|
||||
"file_load.stopping": "이전 작업이 중지되기를 기다리는 중...",
|
||||
"file_load.waiting": "파일 작업 대기 중",
|
||||
"fits the face, e.g. M8": "면에 맞음, 예: M8",
|
||||
@@ -1299,6 +1315,27 @@
|
||||
"home.start_hint": "설계를 만들거나, 최근 파일을 열거나, 예제를 살펴보세요.",
|
||||
"home.whats_new": "새로운 기능",
|
||||
"home.yesterday": "어제",
|
||||
"import_report.details": "세부 정보:",
|
||||
"import_report.file_one": "파일에 다른 문제가 1개 있었습니다.",
|
||||
"import_report.file_other": "파일에 다른 문제가 {count}개 있었습니다.",
|
||||
"import_report.hint": "이 부품을 만든 프로그램에서 다시 내보내면 해결될 수 있습니다. 세부 정보는 경고 메시지 복사에 있습니다.",
|
||||
"import_report.left_out_one": "면 1개를 복구하지 못해 빠져 있습니다.",
|
||||
"import_report.left_out_other": "면 {count}개를 복구하지 못해 빠져 있습니다.",
|
||||
"import_report.needs_repair_one": "부품 1개는 모델링하기 전에 복구해야 합니다.",
|
||||
"import_report.needs_repair_other": "부품 {count}개는 모델링하기 전에 복구해야 합니다.",
|
||||
"import_report.not_imported_one": "부품 1개를 가져오지 못했습니다.",
|
||||
"import_report.not_imported_other": "부품 {count}개를 가져오지 못했습니다.",
|
||||
"import_report.not_shown_one": "면 1개를 그리지 못해 숨겨져 있습니다.",
|
||||
"import_report.not_shown_other": "면 {count}개를 그리지 못해 숨겨져 있습니다.",
|
||||
"import_report.open_one": "부품 1개는 면 사이에 틈이 있어 열린 바디로 들어왔습니다.",
|
||||
"import_report.open_other": "부품 {count}개는 면 사이에 틈이 있어 열린 바디로 들어왔습니다.",
|
||||
"import_report.refused_one": "부품 1개를 솔리드로 만들지 못해 가져오지 않았습니다.",
|
||||
"import_report.refused_other": "부품 {count}개를 솔리드로 만들지 못해 가져오지 않았습니다.",
|
||||
"import_report.repaired_one": "부품 1개의 작은 결함을 복구했습니다.",
|
||||
"import_report.repaired_other": "부품 {count}개의 작은 결함을 복구했습니다.",
|
||||
"import_report.solids": "부품 {parts}개 중 {solids}개를 솔리드로 가져왔습니다.",
|
||||
"import_report.surfaces_one": "부품 1개를 솔리드로 만들지 못해 서피스로 들어왔습니다.",
|
||||
"import_report.surfaces_other": "부품 {count}개를 솔리드로 만들지 못해 서피스로 들어왔습니다.",
|
||||
"install.button_tip": "SolidCraft를 앱으로 설치합니다. 별도 창에서 열리며 오프라인에서도 작동합니다",
|
||||
"install.dock_1": "파일 › Dock에 추가를 선택합니다.",
|
||||
"install.dock_2": "추가를 클릭합니다.",
|
||||
@@ -1535,6 +1572,18 @@
|
||||
"prefs.add_item": "항목 추가",
|
||||
"prefs.alt_frees_manipulators": "Alt / Option 키로 조작 화살표도 자유롭게 움직입니다",
|
||||
"prefs.anti_aliasing_note": "울퉁불퉁한 모서리를 부드럽게 합니다.",
|
||||
"prefs.appearance.colour_vision_help": "의미를 전달하는 색상을 변경합니다: 스케치 상태, 축, 굽힘, 분석 및 아이콘.",
|
||||
"prefs.appearance.desktop_only": "데스크톱 전용",
|
||||
"prefs.appearance.follow_help": "다른 도구가 파일을 다시 쓰면 다시 불러옵니다.",
|
||||
"prefs.appearance.interface_scale": "인터페이스 배율",
|
||||
"prefs.appearance.next_themes": "다음 테마",
|
||||
"prefs.appearance.option_count": "옵션 {count}개",
|
||||
"prefs.appearance.option_count_one": "옵션 {count}개",
|
||||
"prefs.appearance.previous_themes": "이전 테마",
|
||||
"prefs.appearance.theme_count": "테마 {count}개",
|
||||
"prefs.appearance.theme_own": "테마 자체",
|
||||
"prefs.appearance.viewport_colours": "3D 보기 색상",
|
||||
"prefs.appearance.viewport_logo": "뷰포트 및 로고",
|
||||
"prefs.applies_on_restart": "다시 시작하면 적용됩니다",
|
||||
"prefs.assistant": "어시스턴트",
|
||||
"prefs.axes": "축",
|
||||
@@ -1547,6 +1596,8 @@
|
||||
"prefs.bottom_centre": "하단 가운데",
|
||||
"prefs.bottom_right": "오른쪽 아래",
|
||||
"prefs.browser": "브라우저",
|
||||
"prefs.browser_components_open": "구성요소를 펼친 상태로 시작",
|
||||
"prefs.browser_components_open_note": "브라우저의 모든 구성요소가 이전처럼 펼쳐진 상태로 시작합니다. 끄면 직접 펼칠 때까지 접혀 있습니다.",
|
||||
"prefs.browser_eye_left": "표시 아이콘을 접기 화살표 옆에 표시",
|
||||
"prefs.browser_eye_left_note": "브라우저 행의 표시 아이콘을 행 오른쪽 끝의 열이 아니라 이전처럼 접기 화살표 옆에 표시합니다.",
|
||||
"prefs.built_in": "기본 제공",
|
||||
@@ -1591,7 +1642,19 @@
|
||||
"prefs.font_sample": "The quick brown fox · Ø12.50 mm · 45.0° · 0123456789",
|
||||
"prefs.font_unreadable": "선택한 글꼴을 읽을 수 없습니다.",
|
||||
"prefs.general": "일반",
|
||||
"prefs.general_note": "언어, 저장 및 업데이트.",
|
||||
"prefs.general.active": "사용 중",
|
||||
"prefs.general.crash_reports": "충돌 보고서",
|
||||
"prefs.general.edited": "수정됨",
|
||||
"prefs.general.held": "포함 항목",
|
||||
"prefs.general.import": "가져오기...",
|
||||
"prefs.general.last_check": "마지막 확인: {time}",
|
||||
"prefs.general.minutes": "분",
|
||||
"prefs.general.name_setup": "이 설정의 이름",
|
||||
"prefs.general.profile_name": "프로필 이름",
|
||||
"prefs.general.save_new": "새로 저장",
|
||||
"prefs.general.step_import": "STEP 및 가져오기",
|
||||
"prefs.general.this_computer": "이 컴퓨터",
|
||||
"prefs.general_note": "프로필, 언어, 파일 및 업데이트.",
|
||||
"prefs.gesture_orbit": "회전",
|
||||
"prefs.gesture_pan": "이동",
|
||||
"prefs.gesture_zoom": "확대/축소",
|
||||
@@ -1628,10 +1691,10 @@
|
||||
"prefs.live_input": "실시간 입력",
|
||||
"prefs.live_preview": "실시간 미리보기",
|
||||
"prefs.max_file_size": "열 수 있는 최대 파일 크기",
|
||||
"prefs.max_file_size_design": "디자인 파일",
|
||||
"prefs.max_file_size_design": "열 파일의 최대 크기: 디자인",
|
||||
"prefs.max_file_size_flag": "이번 실행은 --max-file-size {size}로 시작했으며 SolidCraft를 닫을 때까지 적용됩니다. 이 설정은 해당 옵션 없이 시작할 때 적용됩니다.",
|
||||
"prefs.max_file_size_note": "기본값: 512 MB 및 256 MB, 최대 16 GB. 파일을 열려면 파일 크기의 몇 배에 달하는 메모리가 필요합니다. 큰 가져오기 데이터를 보관하는 디자인은 저장하고 다시 열기 위해 디자인 파일 한도를 높여야 할 수 있습니다.",
|
||||
"prefs.max_file_size_step": "STEP 및 가져오기 파일",
|
||||
"prefs.max_file_size_step": "열 파일의 최대 크기: STEP 및 가져오기",
|
||||
"prefs.max_file_size_web": "웹 앱은 STEP 및 가져오기 파일을 512 MB까지, 디자인을 256 MB까지 엽니다. 브라우저 탭은 더 큰 파일을 담을 수 없습니다. 데스크톱 앱에서는 이 한도를 높일 수 있습니다.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.minutes": "분",
|
||||
@@ -1643,6 +1706,7 @@
|
||||
"prefs.navigation_note": "마우스, 트랙패드, 3D 마우스로 뷰를 움직이는 방식입니다.",
|
||||
"prefs.navigator_fit": "뷰 탐색기로 뷰를 바꾼 뒤 모델 맞춤",
|
||||
"prefs.navigator_fit_note": "큐브의 면, 모서리, 화살표나 기즈모의 축을 클릭하면 어느 방향에서든 전체가 들어가는 한 거리(경계 구)에서 모델의 중심을 바라봅니다. 켜져 있는 동안 홈 뷰도 같은 확대/축소와 중심을 쓰므로 그 뒤에 고른 뷰는 방향만 바뀝니다. 끄면 뷰가 제자리에서 회전하며 대상과 확대/축소를 유지합니다.",
|
||||
"prefs.network_locations_note": "파일 서버에 디자인을 보관하는 조직용입니다. 끄면 허용한 서버만 사용합니다.",
|
||||
"prefs.not_on_gpu": "이 GPU에서는 사용할 수 없음",
|
||||
"prefs.on": "켜짐",
|
||||
"prefs.opaque": "불투명",
|
||||
@@ -1659,6 +1723,10 @@
|
||||
"prefs.preview_tint_new_faces_help": "바디를 변경하는 피처의 미리 보기에서 추가되는 면을 미리 보기 색으로 그릴 수 있습니다. 끄면 바디가 원래 색으로 그려집니다.",
|
||||
"prefs.print_face_highlight": "출력 바닥면 강조",
|
||||
"prefs.print_face_highlight_tip": "뷰에서 바디의 출력 바닥면을 색칠하고 윤곽을 그리는 강도입니다. 0 %이면 강조하지 않습니다.",
|
||||
"prefs.profile_export": "내보내기",
|
||||
"prefs.profile_imported": "프로필을 가져왔습니다.",
|
||||
"prefs.profile_operations": "작업 프로파일링",
|
||||
"prefs.profile_operations_note": "SMLib 호출, 커널 작업, 피처, 명령, 가져오기에 걸리는 시간을 기록합니다. 에이전트는 diagnostics.profile로 읽습니다.",
|
||||
"prefs.project_options_dialog": "Project 옵션 대화상자",
|
||||
"prefs.project_options_dialog_note": "켜기: 투영 전에 지오메트리 또는 바디와 투영 링크를 선택합니다. 끄기: 기존 직접 선택 작업 흐름을 유지합니다.",
|
||||
"prefs.quality_default": "기본값",
|
||||
@@ -1666,10 +1734,11 @@
|
||||
"prefs.quality_slowest": "가장 느림",
|
||||
"prefs.quality_smoother": "더 부드럽게",
|
||||
"prefs.reports_saved": "{n}개 저장됨",
|
||||
"prefs.restore_page_defaults": "페이지 기본값 복원",
|
||||
"prefs.ribbon_click_ends_tool": "실행 중인 도구의 버튼을 클릭하면 도구 종료",
|
||||
"prefs.ribbon_click_ends_tool_note": "사용 중인 스케치 도구의 강조된 리본 버튼을 클릭하면 도구가 종료되어 뷰에서 클릭하면 다시 선택됩니다. 끄기: 클릭하면 도구가 다시 시작됩니다.",
|
||||
"prefs.right_click": "오른쪽 클릭",
|
||||
"prefs.saving": "자동 저장 및 복구",
|
||||
"prefs.saving": "시작 및 저장",
|
||||
"prefs.scale_note": "창에서 Ctrl + / Ctrl −로도 조절합니다. 버튼은 100%에서 28 px입니다.",
|
||||
"prefs.scale_text": "배율 및 텍스트",
|
||||
"prefs.search": "설정 검색",
|
||||
@@ -1772,6 +1841,12 @@
|
||||
"prefs.under_cursor": "커서 아래",
|
||||
"prefs.units_precision": "단위 및 정밀도",
|
||||
"prefs.updates": "업데이트",
|
||||
"prefs.validation": "바디 검사",
|
||||
"prefs.validation.each_feature": "피처마다 한 번",
|
||||
"prefs.validation.each_step": "모든 단계 후",
|
||||
"prefs.validation.label": "커널 전체 검사",
|
||||
"prefs.validation.off": "끄기 (실험적)",
|
||||
"prefs.validation_note": "모든 단계 후: 스윕, 불리언, 블렌드마다 검사합니다.\n피처마다 한 번: 각 피처의 결과를 한 번 검사합니다.\n끄기 (실험적): 검사하지 않습니다.",
|
||||
"prefs.value_boxes_in_view": "뷰에서 핸들 옆에 값 표시",
|
||||
"prefs.value_boxes_in_view_tip": "명령의 값(거리, 반경 등)을 모델 위 핸들 옆 상자에 표시합니다. 끄면 값은 명령 창에 입력하며, 선택 후 키보드 입력이 그 창으로 가고 모델을 가리는 것이 없습니다.",
|
||||
"prefs.view": "뷰 동작",
|
||||
@@ -1803,6 +1878,17 @@
|
||||
"recovery.just_now": "방금",
|
||||
"recovery.min_ago": "{count}분 전",
|
||||
"recovery.never_saved": "저장된 적 없음",
|
||||
"refused.change": "{body}을(를) 변경하지 못했습니다.",
|
||||
"refused.cut": "{body}을(를) 자르지 못했습니다.",
|
||||
"refused.face_curved": "곡면 중 하나를 모델링에 사용할 수 없습니다.",
|
||||
"refused.face_flat": "평면 중 하나를 모델링에 사용할 수 없습니다.",
|
||||
"refused.face_freeform": "자유 곡면 중 하나를 모델링에 사용할 수 없습니다.",
|
||||
"refused.face_rounded": "둥근 면 중 하나를 모델링에 사용할 수 없습니다.",
|
||||
"refused.faces_apart": "면들이 충분히 맞물리지 않아 모델링에 사용할 수 없습니다.",
|
||||
"refused.intersect": "{body}과(와) 교차하지 못했습니다.",
|
||||
"refused.join": "{body}에 결합하지 못했습니다.",
|
||||
"refused.repair": "부품 복구",
|
||||
"refused.show_face": "면 보기",
|
||||
"render.add_ground": "Add fitted ground to setup",
|
||||
"render.all_shots_label": "모든 샷",
|
||||
"render.appearance_editor.absorption": "흡수",
|
||||
@@ -2800,7 +2886,18 @@
|
||||
"the section's profiles must be in one sketch": "단면의 프로파일은 하나의 스케치 안에 있어야 합니다",
|
||||
"the two origins are on the same component": "두 원점이 같은 구성요소에 있습니다",
|
||||
"thread.automatic_size": "자동 크기",
|
||||
"thread.chamfer_depth_needed": "이 형상에 필요",
|
||||
"thread.designation": "호칭",
|
||||
"thread.drill_range": "탭 제조사의 드릴은 {drill} mm입니다. 암나사의 규격 골지름은 {minor} mm입니다. {low}~{high} mm 드릴을 사용할 수 있습니다.",
|
||||
"thread.drill_range_custom": "드릴은 바깥지름에서 피치를 뺀 {drill} mm입니다. 암나사의 규격 골지름은 {minor} mm입니다. {low}~{high} mm 드릴을 사용할 수 있습니다.",
|
||||
"thread.drill_range_taper": "탭 제조사의 드릴은 {drill} mm입니다. 나사 끝에서의 규격 골지름은 {minor} mm입니다. {low}~{high} mm 드릴을 사용할 수 있습니다.",
|
||||
"thread.end_square": "직각",
|
||||
"thread.fade_short": "1.25 P (ISO 3508)",
|
||||
"thread.fade_standard": "2.5 P (ISO 3508)",
|
||||
"thread.form_bspp": "관용 평행나사 G (ISO 228-1)",
|
||||
"thread.form_bspt": "관용 테이퍼나사 R, Rc (ISO 7-1)",
|
||||
"thread.form_npt": "관용 테이퍼나사 NPT (ASME B1.20.1)",
|
||||
"thread.form_rp": "관용 평행 암나사 Rp (ISO 7-1)",
|
||||
"thread.iso_metric": "ISO 미터",
|
||||
"thread.select_size": "크기 선택",
|
||||
"timeline.feature_hint": "두 번 클릭하여 편집 · 드래그하여 순서 변경 · 마우스 오른쪽 버튼을 클릭하여 더 보기",
|
||||
@@ -2961,6 +3058,7 @@
|
||||
"ui.agent_chat_auth_terminal": "터미널에서 이것을 실행한 뒤 다시 연결을 누르세요:",
|
||||
"ui.agent_chat_auth_title": "에이전트에 로그인",
|
||||
"ui.agent_chat_cancel": "취소",
|
||||
"ui.agent_chat_cancel_timeout": "에이전트가 중지 요청에 응답하지 않아 연결이 끊어졌습니다. 계속하려면 다시 연결하세요.",
|
||||
"ui.agent_chat_cannot_start": "설치되어 있지만 시작되지 않습니다: {error}",
|
||||
"ui.agent_chat_changes": "변경 {n}개, 실행 취소 한 번",
|
||||
"ui.agent_chat_changes_of": "변경 {n}/{limit}",
|
||||
@@ -3026,6 +3124,7 @@
|
||||
"ui.agent_chat_no_control": "에이전트 채팅 엔드포인트가 시작되지 않아 에이전트가 디자인에 접근할 수 없습니다.",
|
||||
"ui.agent_chat_no_images": "이 에이전트는 이미지를 받지 않습니다",
|
||||
"ui.agent_chat_no_picture": "클립보드에 이미지가 없습니다",
|
||||
"ui.agent_chat_node_too_old": "Node.js {version}은 이 에이전트에 사용하기에는 너무 오래된 버전입니다. Node.js 20.10 이상을 설치한 후 새로 고침을 누르세요.",
|
||||
"ui.agent_chat_none_found": "코딩 에이전트를 찾지 못했습니다. 다음 중 하나를 설치한 뒤 새로 고침을 누르세요:",
|
||||
"ui.agent_chat_none_found_short": "에이전트를 찾지 못함",
|
||||
"ui.agent_chat_not_installed": "설치되지 않음",
|
||||
@@ -3232,10 +3331,16 @@
|
||||
"ui.colour_vision_sample": "예시",
|
||||
"ui.colour_vision_tritan": "청색각이상 (Tritan)",
|
||||
"ui.colour_vision_tritan_tip": "파란색 감각이 약하거나 없는 경우",
|
||||
"ui.comb_length": "빗 길이",
|
||||
"ui.comb_length_tip": "빗살의 길이: 자동 길이의 배수입니다. 자동 길이에서는 가장 긴 빗살이 표시된 곡선 크기의 5분의 1이 됩니다.",
|
||||
"ui.comb_teeth": "빗살 수",
|
||||
"ui.comb_teeth_tip": "각 빗의 빗살 수이며, 곡선을 따라 같은 간격으로 놓입니다. 빗살이 많을수록 곡률이 어떻게 바뀌는지 더 자세히 보입니다.",
|
||||
"ui.combine": "결합",
|
||||
"ui.coming_later": "추후 지원 예정",
|
||||
"ui.command_done": "{command} 완료",
|
||||
"ui.command_palette_hint": "명령 입력(예: {example}) — 또는 S 키를 눌러 검색",
|
||||
"ui.components_from_bodies_hint": "활성 구성요소 아래에 선택한 각 바디와 같은 이름의 구성요소를 만듭니다.",
|
||||
"ui.components_from_bodies_required": "바디를 하나 이상 선택하세요.",
|
||||
"ui.concentric": "동심",
|
||||
"ui.cone": "원뿔",
|
||||
"ui.conic_curve": "원뿔 곡선",
|
||||
@@ -3254,11 +3359,24 @@
|
||||
"ui.construction.ribbon_label": "보조선",
|
||||
"ui.construction.side_hint": "보조 직선: 선이 뻗어 나갈 쪽 클릭; 마우스 오른쪽 버튼으로 다시 시작",
|
||||
"ui.context_menu": "컨텍스트 메뉴",
|
||||
"ui.continuity_crease": "꺾임",
|
||||
"ui.continuity_curvature": "곡률",
|
||||
"ui.continuity_curvature_change": "곡률 변화",
|
||||
"ui.continuity_curvature_change_tip": "곡률 변화(G3): 이음매에서 곡률의 변화도 부드럽습니다.",
|
||||
"ui.continuity_curvature_tip": "곡률 연속(G2): 반사가 꺾임 없이 이어집니다.",
|
||||
"ui.continuity_g0": "G0 꺾임",
|
||||
"ui.continuity_g1": "G1 접선",
|
||||
"ui.continuity_g2": "G2 곡률",
|
||||
"ui.continuity_gap": "틈",
|
||||
"ui.continuity_position": "위치",
|
||||
"ui.continuity_position_tip": "위치(G0): 면이 만나지만, 만나는 곳에 꺾임이 있습니다.",
|
||||
"ui.continuity_smooth_curvature_change": "부드러운 곡률 변화",
|
||||
"ui.continuity_smooth_curvature_change_tip": "부드러운 곡률 변화(G4): 이음매에서 곡률이 변하는 방식까지 부드럽게 이어집니다.",
|
||||
"ui.continuity_tangent": "접선",
|
||||
"ui.continuity_tangent_tip": "접선(G1): 꺾임은 없지만, 이음매에서 곡률이 급변합니다.",
|
||||
"ui.copy": "복사",
|
||||
"ui.copy_details": "세부 정보 복사",
|
||||
"ui.copy_details_tip": "찾아보거나 보고할 수 있도록 전체 메시지를 복사합니다.",
|
||||
"ui.corner": "코너",
|
||||
"ui.corners": "코너",
|
||||
"ui.counterbore": "카운터보어",
|
||||
@@ -3373,6 +3491,7 @@
|
||||
"ui.edit_sketch_empty": "편집할 스케치가 없습니다",
|
||||
"ui.ellipse": "타원",
|
||||
"ui.emboss_wrap_note": "지원되는 원통에 접하는 직교 선 프로파일은 자동으로 감깁니다. 대상을 고르려면 벽을 선택하세요.",
|
||||
"ui.end": "끝",
|
||||
"ui.end_radius": "끝 반지름",
|
||||
"ui.end_scale": "끝 배율",
|
||||
"ui.entities": "요소",
|
||||
@@ -3397,11 +3516,39 @@
|
||||
"ui.extent_type": "범위 유형",
|
||||
"ui.extrude": "돌출",
|
||||
"ui.factor": "배율",
|
||||
"ui.fade": "페이드",
|
||||
"ui.fade_length": "페이드 길이",
|
||||
"ui.feature_history": "피처 기록",
|
||||
"ui.feature_options": "피처 옵션",
|
||||
"ui.features": "피처",
|
||||
"ui.file": "파일",
|
||||
"ui.fillet": "필렛",
|
||||
"ui.fillet_corner_ball": "볼",
|
||||
"ui.fillet_corner_ball_tip": "굴러가는 볼의 구면 일부로, 각 라운드에 접합니다(G1).",
|
||||
"ui.fillet_corner_curvature_tip": "곡률(G2): 부드러운 패치. 코너를 각 모서리를 따라 셋백만큼(입력하지 않으면 반지름의 {factor}배) 물려 패치로 채우므로, 반사가 꺾임 없이 지나갑니다.",
|
||||
"ui.fillet_corner_needs_constant": "부드러운 패치 코너(G2)에는 일정한 반지름과 정확한 반지름 맞춤이 필요합니다.",
|
||||
"ui.fillet_corner_patch": "부드러운 패치",
|
||||
"ui.fillet_corner_patch_tip": "곡률 연속(G2): 코너를 각 모서리를 따라 물려 패치로 채우므로, 반사가 꺾임 없이 지나갑니다.",
|
||||
"ui.fillet_corner_tangent_tip": "접선(G1): 굴러가는 볼 코너. 굴러가는 볼의 구면 일부로, 각 라운드에 접합니다.",
|
||||
"ui.fillet_error_corner": "여기에서는 부드러운 코너를 만들 수 없었습니다. 다른 셋백이나 접선 코너를 시도해 보세요.",
|
||||
"ui.fillet_error_failed": "이 모서리들에 이 필렛을 만들 수 없었습니다. 다른 반지름을 시도하거나 한 번에 둥글게 하는 모서리 수를 줄여 보세요.",
|
||||
"ui.fillet_error_setback_short": "이 코너의 패치에 비해 셋백이 너무 짧습니다. {least}부터 만들 수 있습니다. 더 긴 셋백을 시도해 보세요.",
|
||||
"ui.fillet_error_too_large": "이 모서리 옆의 면에 비해 반지름이 너무 큽니다. 더 작은 반지름을 시도하거나 '면 경계'를 '최대 반지름'으로 설정하세요.",
|
||||
"ui.fillet_fit": "면 경계",
|
||||
"ui.fillet_fit_exact": "정확한 반지름",
|
||||
"ui.fillet_fit_needs_constant": "반지름 맞춤에는 일정한 반지름이 필요합니다.",
|
||||
"ui.fillet_fit_reduce": "최대 반지름",
|
||||
"ui.fillet_fit_taper": "좁힌 반지름",
|
||||
"ui.fillet_fit_tip": "반지름이 모서리 옆 면이 허용하는 것보다 클 때.\n정확한 반지름: 지정한 반지름 그대로, 또는 둥글게 하지 않고 이유를 알리는 메시지.\n최대 반지름: 모든 모서리를 들어가는 가장 큰 반지름으로 둥글게 합니다.\n좁힌 반지름: 반지름을 유지하고 면이 모자란 곳에서만 라운드를 좁힙니다.",
|
||||
"ui.fillet_options_tip": "접선 체인: 선택한 모서리에서 부드럽게 이어지는 모서리도 둥글게 합니다.\n미리 보기: 편집하는 동안 결과를 뷰에 표시합니다.",
|
||||
"ui.fillet_setback": "셋백",
|
||||
"ui.fillet_setback_hint": "{factor} × 반지름",
|
||||
"ui.fillet_setback_least": "{least}보다 크게",
|
||||
"ui.fillet_setback_tip": "부드러운 코너 패치가 시작되는 위치: 코너에서 각 모서리를 따른 거리. 기본값은 반지름의 {factor}배. 최소 셋백(셋백 없이 라운드가 만나는 곳, 면이 서로 직각이면 반지름)보다 커야 합니다.",
|
||||
"ui.fillet_smooth_radius": "반지름 부드럽게 변화",
|
||||
"ui.fillet_type_choices_tip": "일정한 반지름: 모서리 전체에 하나의 반지름.\n현 길이: 면이 어떤 각도로 만나든 라운드의 폭이 같습니다.\n가변 반지름: 양 끝의 반지름과, 추가하면 모서리를 따라 더.\n곡률 연속: 평면과 만나는 곳에서 반사가 꺾이지 않는 단면(G2).\n스플라인: 모서리를 따라 반지름이 설정한 제어 반지름을 따릅니다.",
|
||||
"ui.fillet_type_curvature": "곡률 연속",
|
||||
"ui.fillet_type_tip": "모서리를 가로지르는 단면. 곡률 연속: 평면 사이의 곡률 연속(G2) 단면으로, 반사가 면에서 라운드로 꺾임 없이 이어집니다.",
|
||||
"ui.filter_commands_or_keys": "명령 또는 키 필터",
|
||||
"ui.findInTimeline": "타임라인에서 찾기",
|
||||
"ui.findInWindow": "창에서 찾기",
|
||||
@@ -3634,6 +3781,7 @@
|
||||
"ui.part_size_needs_a_part": "크기를 바꿀 삽입된 표준 부품(나사, 너트, 와셔 또는 핀)을 선택하십시오. 바디를 키우거나 줄이려면 축척을 사용하십시오.",
|
||||
"ui.part_size_shared": "부품을 다시 빌드하며 추가된 피처는 모두 대체됩니다. 이 구성요소의 모든 인스턴스가 업데이트됩니다.",
|
||||
"ui.paste": "붙여넣기",
|
||||
"ui.patch_continuity_corner_tip": "패치는 각 변을 따라 주변 면과 선택한 연속성으로 이어집니다. 주변 면끼리 서로 다른 곡률로 만나는 모서리에서는 그곳에서 접선(G1)으로 유지됩니다.",
|
||||
"ui.path": "경로",
|
||||
"ui.pattern_object_type_tip": "바디: 뷰나 브라우저에서 선택하는 솔리드 형상이며, 각각 통째로 복사됩니다.\n피처: 돌출이나 구멍 같은 기록의 단계이며, 기록에서 또는 그 단계가 만든 면에서 선택합니다. 그 단계가 반복됩니다.\n스케치는 둘 다 아닙니다. 곡선은 스케치 안의 패턴이나 대칭 복사로 반복하거나, 먼저 돌출하세요.",
|
||||
"ui.pattern_on_path": "경로 패턴",
|
||||
@@ -3792,6 +3940,7 @@
|
||||
"ui.slice": "슬라이스",
|
||||
"ui.slicer": "슬라이서",
|
||||
"ui.slicer_delete_tip": "{program}\nDelete 키로 목록에서 제거",
|
||||
"ui.slicer_single_instance_tip": "실행 중인 창에 파일을 불러오는 슬라이서용입니다.",
|
||||
"ui.slot": "슬롯",
|
||||
"ui.smooth": "부드럽게",
|
||||
"ui.smooth_wheel_zoom": "부드러운 휠 확대/축소",
|
||||
@@ -3921,13 +4070,13 @@
|
||||
"update.available": "SolidCraft {version} 사용 가능",
|
||||
"update.button": "업데이트",
|
||||
"update.button_version": "{version}(으)로 업데이트",
|
||||
"update.check_automatically": "업데이트 자동 확인",
|
||||
"update.check_automatically": "시작 시 및 하루에 한 번 확인",
|
||||
"update.check_for_updates": "업데이트 확인",
|
||||
"update.check_now": "지금 확인",
|
||||
"update.check_unavailable": "이 설치에서는 업데이트를 확인할 수 없습니다.",
|
||||
"update.checking": "확인 중…",
|
||||
"update.copy": "복사",
|
||||
"update.dmg_opened": "디스크 이미지를 확인하고 열었습니다: SolidCraft를 응용 프로그램 폴더로 끌어 기존 버전을 바꾼 뒤 SolidCraft를 다시 시작하세요.",
|
||||
"update.dmg_opened": "디스크 이미지를 확인하고 열었습니다: SolidCraft를 종료하고, 새 SolidCraft를 응용 프로그램 폴더로 끌어 기존 버전을 바꾼 뒤 SolidCraft를 다시 여세요.",
|
||||
"update.download": "다운로드",
|
||||
"update.download_open": "다운로드 후 열기",
|
||||
"update.downloading": "다운로드 및 확인 중… {done} / {total} MB",
|
||||
|
||||
+85
-4
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "Segi empat tepat",
|
||||
"Rectangular Tube": "Tiub Segi Empat Tepat",
|
||||
"Reference face": "Muka rujukan",
|
||||
"Reference plane": "Satah rujukan",
|
||||
"Relief Depth": "Kedalaman Pelepasan",
|
||||
"Relief Width": "Lebar Pelepasan",
|
||||
"Remove Tools": "Buang Alat",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "Keratan",
|
||||
"Section (optional)": "Keratan (pilihan)",
|
||||
"Section position": "Kedudukan keratan",
|
||||
"Sections": "Keratan",
|
||||
"Sections (one sketch each)": "Keratan (satu lakaran setiap satu)",
|
||||
"Select Other": "Pilih Yang Lain",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Pilih Lain: tahan butang kiri pada model tanpa bergerak (atau klik kanan › Pilih Lain…) untuk senarai objek di bawah kursor. Klik tempat sama untuk objek seterusnya. Klik dua kali butang tengah untuk ke Utama (Tetapan › Navigasi).",
|
||||
@@ -859,6 +861,7 @@
|
||||
"cmd.component_extract": "Ekstrak ke Fail",
|
||||
"cmd.component_finish_edit": "Selesai Sunting",
|
||||
"cmd.component_from_bodies": "Cipta Komponen daripada Jasad",
|
||||
"cmd.component_hide_history": "Sembunyikan Sejarah Komponen",
|
||||
"cmd.component_insert_design": "Sisip Reka Bentuk",
|
||||
"cmd.component_links": "Senarai Pautan",
|
||||
"cmd.component_list": "Senarai Komponen",
|
||||
@@ -875,6 +878,7 @@
|
||||
"cmd.component_rename": "Tukar Nama Komponen",
|
||||
"cmd.component_replace_with_link": "Gantikan dengan Pautan",
|
||||
"cmd.component_revert_position": "Kembalikan Kedudukan",
|
||||
"cmd.component_show_history": "Papar Sejarah Komponen",
|
||||
"cmd.component_unlock": "Buka Kunci Kandungan",
|
||||
"cmd.component_update_links": "Kemas Kini Pautan",
|
||||
"cmd.config_activate": "Aktifkan Konfigurasi",
|
||||
@@ -885,6 +889,7 @@
|
||||
"cmd.config_rule": "Peraturan Konfigurasi",
|
||||
"cmd.config_rules": "Peraturan Konfigurasi",
|
||||
"cmd.config_table": "Paparkan Jadual Konfigurasi",
|
||||
"cmd.construct.plane.axis_point": "Satah Melalui Paksi dan Titik",
|
||||
"cmd.construct_axis_edge": "Paksi Melalui Tepi",
|
||||
"cmd.construct_axis_normal_to_face": "Paksi Serenjang dengan Muka",
|
||||
"cmd.construct_axis_through_cylinder": "Paksi Melalui Silinder/Kon/Torus",
|
||||
@@ -933,6 +938,11 @@
|
||||
"cmd.explode_show": "Papar Pandangan Terlerai",
|
||||
"cmd.expr_evaluate": "Nilaikan Ungkapan",
|
||||
"cmd.face_delete": "Padam Muka",
|
||||
"cmd.file.export_3mf": "Eksport 3MF",
|
||||
"cmd.file.export_obj": "Eksport OBJ",
|
||||
"cmd.file.export_step": "Eksport STEP",
|
||||
"cmd.file.export_stl": "Eksport STL",
|
||||
"cmd.file.export_urdf": "Eksport URDF",
|
||||
"cmd.file_export": "Eksport",
|
||||
"cmd.file_insert_mesh": "Sisip Mesh",
|
||||
"cmd.file_insert_step": "Sisip STEP",
|
||||
@@ -950,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "Aktifkan Warna Komponen",
|
||||
"cmd.inspect_colored_components_disable": "Nyahaktifkan Warna Komponen",
|
||||
"cmd.inspect_compare": "Bandingkan Jasad",
|
||||
"cmd.inspect_compare_picture": "Banding dengan Imej",
|
||||
"cmd.inspect_continuity": "Analisis Keselanjaran",
|
||||
"cmd.inspect_curvature_comb": "Analisis Sisir Kelengkungan",
|
||||
"cmd.inspect_curvature_map": "Analisis Peta Kelengkungan",
|
||||
@@ -1040,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "Sunting Awan Titik",
|
||||
"cmd.pointcloud_insert": "Sisip Awan Titik",
|
||||
"cmd.pointcloud_list": "Awan Titik",
|
||||
"cmd.render.compare.divider": "Alih Pembahagi Perbandingan Render",
|
||||
"cmd.render.result.discard": "Buang Hasil Render",
|
||||
"cmd.render.result.select": "Pilih Hasil Render",
|
||||
"cmd.render_aging_describe": "Penuaan Permukaan Berlapis",
|
||||
"cmd.render_backends": "Enjin Render",
|
||||
"cmd.render_batch": "Render Semua Syot",
|
||||
@@ -1254,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "Alih Penanda Garis Masa",
|
||||
"cmd.timeline_stop": "Hentikan Main Sejarah",
|
||||
"cmd.timeline_suppress": "Nyahaktifkan Ciri",
|
||||
"cmd.ui.assistant": "Togol Pembantu",
|
||||
"cmd.view.render_browser": "Pelayar Render",
|
||||
"cmd.view.render_dock": "Alih Dok Render",
|
||||
"cmd.view.render_focus": "Pandangan Fokus Render",
|
||||
@@ -1288,9 +1303,11 @@
|
||||
"cmd.view_tool": "Alat navigasi",
|
||||
"cmd.view_top": "Pandangan Atas",
|
||||
"cmd.view_visual_style": "Gaya visual",
|
||||
"color.blue": "Biru",
|
||||
"color.copy": "Klik untuk menyalin nilai warna",
|
||||
"color.edit": "Klik untuk menyunting warna",
|
||||
"color.gamma": "Memaparkan nilai warna dalam ruang gamma 0-255",
|
||||
"color.green": "Hijau",
|
||||
"color.hex": "Heks",
|
||||
"color.hex_help": "Masukkan #rrggbb atau #rrggbbaa; enam digit mengekalkan kelegapan",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1298,6 +1315,7 @@
|
||||
"color.linear": "Memaparkan nilai warna dalam ruang linear 0-1",
|
||||
"color.recent": "Warna terkini",
|
||||
"color.recent_help": "Warna yang anda pilih dalam mana-mana medan warna dipaparkan di sini",
|
||||
"color.red": "Merah",
|
||||
"color.saturation": "Ketepuan (peratus)",
|
||||
"color.saturation_value": "Ketepuan dan nilai",
|
||||
"color.selected": "Warna dipilih",
|
||||
@@ -1305,6 +1323,8 @@
|
||||
"color.step_up": "Naikkan",
|
||||
"color.value": "Nilai, iaitu kecerahan (peratus)",
|
||||
"config.column_filter_empty": "Tiada lajur yang sepadan",
|
||||
"config.rule_parameter_columns": "Hanya parameter yang mempunyai lajur dalam jadual disenaraikan. Tambah satu dengan Tambah Lajur (+) untuk menggunakannya di sini.",
|
||||
"config.rule_suppression_columns": "Hanya ciri yang mempunyai lajur Nyahaktifkan dalam jadual disenaraikan. Tambah satu dengan Tambah Lajur (+) untuk mengawalnya di sini.",
|
||||
"contact.components_count": "{count} komponen",
|
||||
"contact.enabled_label": "diaktifkan",
|
||||
"coupling.gear_1": "Gear 1",
|
||||
@@ -1353,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "Pilih jasad atau ciri: lakaran bukan kedua-duanya. Ulang lengkungnya dengan Corak atau Cermin milik lakaran itu sendiri, atau semperit dahulu.",
|
||||
"dialog.select": "↖ Pilih",
|
||||
"dialog.selected_count": "↖ {count} dipilih",
|
||||
"dialog.selection_constraint": "Kekangan {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · lengkung {id}",
|
||||
"dialog.selection_guidance": "Pilih input untuk diubah. Klik objek dipilih untuk nyahpilih; Ctrl/Cmd+klik juga menogol pilihan. Semak pratonton, kemudian tekan OK.",
|
||||
"dialog.selection_midpoint": "Titik tengah · {id}",
|
||||
"dialog.selection_profile": "{sketch} · profil {number}",
|
||||
"dialog.selection_step": "{step} daripada {total}: Pilih {input}",
|
||||
"dialog.step": "Langkah",
|
||||
"document.changed_on_disk": "{name} berubah pada cakera",
|
||||
@@ -1436,6 +1460,7 @@
|
||||
"help.construct.axis.two_points": "Mencipta paksi binaan melalui dua titik, bucu atau titik lakaran. Gunakan untuk paksi yang menghubungkan dua kedudukan yang diketahui.",
|
||||
"help.construct.plane.along_path": "Mencipta satah binaan yang serenjang dengan tepi atau lengkung, pada satu kedudukan sepanjangnya. Gunakan untuk melakar profil bagi Sapu atau keratan sesuatu laluan.",
|
||||
"help.construct.plane.at_angle": "Mencipta satah binaan yang dipusingkan pada satu sudut daripada satah atau muka, pada paksi atau tepi. Gunakan untuk lakaran condong, seperti braket bersudut.",
|
||||
"help.construct.plane.axis_point": "Mencipta satah binaan yang mengandungi satu paksi dan satu titik di luar paksi itu. Pilih titik asal atau paksi binaan, tepi lurus atau garis lakaran, kemudian bucu, titik lakaran atau titik binaan. Titik pada paksi tidak dapat menentukan satu satah yang unik.",
|
||||
"help.construct.plane.midplane": "Mencipta satah binaan di tengah-tengah antara dua satah atau muka rata, atau yang membahagi dua kedua-duanya di tempat ia bertemu pada satu sudut. Gunakan sebagai satah simetri untuk pencerminan.",
|
||||
"help.construct.plane.offset": "Mencipta satah binaan yang selari dengan satah atau muka rata, pada satu jarak atau melalui satu titik. Gunakan untuk melakar pada ketinggian tertentu di atas sesuatu muka.",
|
||||
"help.construct.plane.perpendicular": "Mencipta satah binaan yang memuatkan garis atau tepi dan berdiri serenjang dengan satah atau muka. Gunakan untuk melakar secara tegak pada model sepanjang tepi.",
|
||||
@@ -1812,6 +1837,8 @@
|
||||
"joint_move.blocked_hint": "Semak jenis sendi (seperti Tegar), status tetap, had sendi dan nilai yang dipacu parameter.",
|
||||
"joint_move.blocked_title": "Tidak dapat mengalihkan bahagian ini",
|
||||
"joint_move.coarse": "Langkah putaran dekat paksi (°)",
|
||||
"joint_move.conflict": "Satu sendi bagi bahagian ini bercanggah dengan yang lain: {joint}.",
|
||||
"joint_move.conflict_hint": "Sunting, nyahaktifkan atau padam sendi yang bercanggah supaya sendi lain dapat meletakkan bahagian itu.",
|
||||
"joint_move.distance": "Jarak dari mula",
|
||||
"joint_move.done": "Selesai",
|
||||
"joint_move.fine": "Langkah putaran jauh dari paksi (°)",
|
||||
@@ -1911,6 +1938,8 @@
|
||||
"motion.track_name": "Trek {number}",
|
||||
"nav.alias.help": "Shift+Alt+seret kiri mengorbit, Shift+Alt+seret tengah menyorot, Shift+Alt+seret kanan menzum; seret tengah juga menyorot dan seret kanan mengorbit",
|
||||
"nav.blender.help": "Seret tengah mengorbit, Shift+tengah menyorot, Ctrl+tengah menzum; seret kanan tidak menggerakkan kamera",
|
||||
"nav.creo.help": "Seret tengah mengorbit, Shift+tengah menyorot, Ctrl+seret tengah secara menegak menzum (ke bawah untuk zum masuk); tatal roda untuk zum",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "Tetikus dan tetikus 3D berputar melalui kedua-dua kutub dan mengekalkan gelek. Pelekapan paksi dengan Shift dilumpuhkan.",
|
||||
"nav.free_orbit.label": "Orbit bebas",
|
||||
"nav.free_orbit.snap_help": "Tetikus dan tetikus 3D berputar melalui kedua-dua kutub dan mengekalkan gelek. Tahan Shift semasa menyeret orbit untuk melekapkan arah pandangan dan gelek.",
|
||||
@@ -2063,12 +2092,19 @@
|
||||
"parts.family.button_head_cap_screw": "Skru kepala butang",
|
||||
"parts.family.dowel_pin": "Pin selari (dowel)",
|
||||
"parts.family.hex_bolt": "Bolt / skru kepala heks",
|
||||
"parts.family.hex_cap_screw_inch": "Skru kepala heks (cap screw, inci)",
|
||||
"parts.family.hex_nut": "Nat heks",
|
||||
"parts.family.hex_nut_inch": "Nat heks (inci)",
|
||||
"parts.family.set_screw": "Skru penetap soket heks, hujung rata",
|
||||
"parts.family.socket_head_cap_screw": "Skru kepala soket",
|
||||
"parts.family.washer": "Sesendal biasa",
|
||||
"parts.family.washer_sae": "Sesendal biasa, SAE (sempit)",
|
||||
"parts.family.washer_uss": "Sesendal biasa, USS (lebar)",
|
||||
"parts.holes": "Lubang (pilihan)",
|
||||
"parts.placement_hint": "pilih satu atau lebih lubang untuk meletakkan bahagian pada setiap satu, atau tekan OK untuk meletakkannya di titik asal",
|
||||
"parts.system": "Sistem unit",
|
||||
"parts.system.inch": "Inci",
|
||||
"parts.system.metric": "Metrik",
|
||||
"pattern.alignment_2": "Penjajaran 2",
|
||||
"pattern.alignment_hint": "Bilangan termasuk asal. Tengah seimbangkan salinan; Balik pilih sisi lebih untuk bilangan genap.",
|
||||
"pattern.direction_1": "Arah 1",
|
||||
@@ -2130,6 +2166,8 @@
|
||||
"prefs.button_left": "Seret kiri",
|
||||
"prefs.button_middle": "Seret tengah",
|
||||
"prefs.button_right": "Seret kanan",
|
||||
"prefs.carousel_next": "Seterusnya",
|
||||
"prefs.carousel_previous": "Sebelumnya",
|
||||
"prefs.choose_font": "Pilih fail fon…",
|
||||
"prefs.click": "Klik",
|
||||
"prefs.contact_shading": "Lorekan sentuhan lembut",
|
||||
@@ -2142,6 +2180,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Permukaan melengkung",
|
||||
"prefs.curves": "Permukaan melengkung",
|
||||
"prefs.cut_preview_tint": "Papar bahan yang dibuang dalam warna merah",
|
||||
"prefs.cut_preview_tint_help": "Pratonton mengisi bahan yang dibuang oleh sesuatu ciri, dan bentuk yang digantikannya, dengan bayang merah lut sinar. Apabila dimatikan, isian itu telus sepenuhnya dan hanya garis luarnya dilukis.",
|
||||
"prefs.dark_parts_shading": "Kekalkan bentuk bahagian yang gelap",
|
||||
"prefs.dark_parts_shading_help": "Lorekkan pandangan dalam cahaya linear dengan cahaya isian langit yang lembut, supaya muka yang gelap mengekalkan kecerunan dan tidak menjadi hitam",
|
||||
"prefs.dead_zone_note": "Jalur berlorek ialah zon mati: gerakan di dalamnya diabaikan.",
|
||||
@@ -2200,6 +2240,8 @@
|
||||
"prefs.interface_language": "Bahasa antara muka",
|
||||
"prefs.interface_note": "Rupa SolidCraft dan kedudukan bahagiannya.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_curved_roll_arrows": "Songsangkan anak panah gelek melengkung",
|
||||
"prefs.invert_curved_roll_arrows_note": "Dua anak panah melengkung di sudut kubus menggelek pandangan dalam satah skrin. Hidup: anak panah ikut arah jam menggelek lawan arah jam dan anak panah lawan arah jam menggelek ikut arah jam. Mati: setiap anak panah menggelek pandangan mengikut arah lengkungannya.",
|
||||
"prefs.invert_navigator_arrows": "Songsangkan anak panah putaran kubus pandangan",
|
||||
"prefs.invert_navigator_arrows_note": "Anak panah di sekeliling kubus mengorbit kamera mengikut arah yang ditunjuknya, seperti dalam Fusion dan Inventor: anak panah di kiri menunjuk ke kanan dan mengayunkan kamera ke kanan. Mati: anak panah memutarkan kubus ke arah sisi tempat ia berada, jadi anak panah kiri mengayunkan kamera ke kiri. Anak panah gelek yang melengkung tidak terjejas.",
|
||||
"prefs.keep_work_on_tab_switch": "Kekalkan dialog dan alat terbuka apabila menukar reka bentuk",
|
||||
@@ -2259,6 +2301,8 @@
|
||||
"prefs.placement": "Penempatan",
|
||||
"prefs.prefer_free_sketch_points": "Utamakan titik lakaran yang boleh digerakkan apabila titik bertindih",
|
||||
"prefs.preview_choices": "Pratonton pilihan anda",
|
||||
"prefs.preview_tint_new_faces": "Warnakan permukaan baharu dalam pratonton",
|
||||
"prefs.preview_tint_new_faces_help": "Pratonton ciri yang mengubah sesuatu jasad boleh melukis permukaan yang ditambahnya dalam warna pratonton. Apabila dimatikan, jasad dilukis seperti biasa.",
|
||||
"prefs.print_face_highlight": "Serlahan muka cetakan",
|
||||
"prefs.print_face_highlight_tip": "Sejauh mana muka cetakan sesuatu jasad diwarnakan dan digaris dalam paparan. 0 % tidak menunjukkan serlahan.",
|
||||
"prefs.profile_active": "Profil aktif",
|
||||
@@ -2556,6 +2600,10 @@
|
||||
"render.base_color": "Warna asas · sRGB",
|
||||
"render.base_metallic": "Pecahan logam",
|
||||
"render.base_opaque_only": "Penyunting ini mengekalkan semantik jasad legap. Gunakan kawalan kaca yang khusus untuk transmisi optik.",
|
||||
"render.batch_all_shots": "Semua syot ({count})",
|
||||
"render.batch_empty": "Simpan satu syot sebelum merender semua syot.",
|
||||
"render.batch_limit": "Render Semua Syot dihadkan kepada 32 syot.",
|
||||
"render.batch_status": "{running} sedang berjalan · {waiting} menunggu",
|
||||
"render.bloom_enable": "Papar bloom",
|
||||
"render.bloom_help": "Pendaran kreatif untuk keluaran sahaja daripada serlahan linear positif, disebarkan dengan tiga laluan kotak ternormal. Jejari dalam piksel keluaran. Ditambah dalam cahaya paparan linear sebelum sRGB; bukan kesan kanta, kesan volumetrik atau peningkatan penumpuan. EXR mentah tidak berubah.",
|
||||
"render.bloom_opaque": "Bloom memerlukan keluaran legap. PNG telus ditolak, bukan diratakan secara senyap.",
|
||||
@@ -2666,6 +2714,13 @@
|
||||
"render.choose_look": "Pilih gaya dalam Penampilan dahulu",
|
||||
"render.chromatic_offset": "Ofset tepi kromatik",
|
||||
"render.clear_stage": "Buang pentas yang disimpan",
|
||||
"render.compare_divider": "Pembahagi perbandingan",
|
||||
"render.compare_live": "Langsung",
|
||||
"render.compare_live_preview": "Pratonton langsung",
|
||||
"render.compare_render": "Render {number}",
|
||||
"render.compare_side": "Render {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} s · {renderer} · dirakam pada semakan {revision}",
|
||||
"render.compare_swap": "Tukar",
|
||||
"render.copy_request": "Salin JSON",
|
||||
"render.cpu_aa_help": "Sampel ruang: lebih banyak sampel menambah baik tepi dan menambah masa render.",
|
||||
"render.cpu_area_add": "Tambah lampu segi empat tepat CPU",
|
||||
@@ -3415,6 +3470,10 @@
|
||||
"render.result_retained": "Memaparkan imej siap yang terakhir; render yang lebih baharu belum menggantikannya.",
|
||||
"render.result_sequence": "Titik Semak",
|
||||
"render.result_title": "Hasil Render",
|
||||
"render.results_choose": "Hasil yang dikekalkan",
|
||||
"render.results_label": "Render {number} · {shot} · {seconds} s",
|
||||
"render.results_stale_label": "{result} [lapuk]",
|
||||
"render.results_unnamed": "Imej",
|
||||
"render.return_design": "Kembali ke Reka Bentuk",
|
||||
"render.review_stale": "Tinjauan ini sudah lapuk. Selesaikan atau semak semula sebelum menggunakan.",
|
||||
"render.ribbon_aging": "Penuaan",
|
||||
@@ -3898,10 +3957,7 @@
|
||||
"the two origins are on the same component": "kedua-dua titik asal berada pada komponen yang sama",
|
||||
"thread.automatic_size": "Saiz automatik",
|
||||
"thread.designation": "Penamaan",
|
||||
"thread.direction": "Arah bebenang",
|
||||
"thread.iso_metric": "Metrik ISO",
|
||||
"thread.left_hand": "Tangan kiri",
|
||||
"thread.right_hand": "Tangan kanan",
|
||||
"thread.select_size": "Pilih saiz",
|
||||
"timeline.delete_after_marker_one": "Padam 1 Ciri Selepas Penanda Sejarah",
|
||||
"timeline.delete_after_marker_other": "Padam {count} Ciri Selepas Penanda Sejarah",
|
||||
@@ -4075,6 +4131,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Jalankan arahan ini dalam terminal, kemudian tekan Sambung semula:",
|
||||
"ui.agent_chat_auth_title": "Log masuk ke ejen",
|
||||
"ui.agent_chat_cancel": "Batal",
|
||||
"ui.agent_chat_cancel_timeout": "Ejen tidak bertindak balas kepada Hentikan dan telah diputuskan sambungannya. Sambung semula untuk meneruskan.",
|
||||
"ui.agent_chat_cannot_start": "Terpasang, tetapi tidak bermula: {error}",
|
||||
"ui.agent_chat_changes": "{n} perubahan, satu Buat Asal",
|
||||
"ui.agent_chat_changes_n": "{n} perubahan",
|
||||
@@ -4084,11 +4141,13 @@
|
||||
"ui.agent_chat_command": "Arahan ejen",
|
||||
"ui.agent_chat_connect": "Sambung",
|
||||
"ui.agent_chat_copy": "Salin",
|
||||
"ui.agent_chat_copy_message": "Salin mesej",
|
||||
"ui.agent_chat_ctx_bodies": "Jasad",
|
||||
"ui.agent_chat_ctx_elements": "Elemen",
|
||||
"ui.agent_chat_ctx_features": "Ciri",
|
||||
"ui.agent_chat_ctx_more": "+{n} lagi",
|
||||
"ui.agent_chat_ctx_other": "Lain-lain",
|
||||
"ui.agent_chat_custom_heading": "Model tersuai",
|
||||
"ui.agent_chat_custom_hint": "Program yang guna Agent Client Protocol melalui stdin dan stdout. Dijalankan tanpa shell.",
|
||||
"ui.agent_chat_denied": "Ditolak",
|
||||
"ui.agent_chat_deny": "Tolak",
|
||||
@@ -4132,8 +4191,16 @@
|
||||
"ui.agent_chat_mode_all": "Semua",
|
||||
"ui.agent_chat_mode_ask": "Tanya",
|
||||
"ui.agent_chat_mode_edits": "Suntingan",
|
||||
"ui.agent_chat_model_custom": "Guna tersuai: {model}",
|
||||
"ui.agent_chat_model_no_tools": "{model} tidak dapat menggunakan alat SolidCraft, jadi hanya boleh berbual. Pilih model yang menyokong alat.",
|
||||
"ui.agent_chat_model_rejected": "Ejen tidak menerima {model}: {error}",
|
||||
"ui.agent_chat_model_search": "Cari model…",
|
||||
"ui.agent_chat_model_tip": "Model yang digunakan oleh ejen. Pilihan anda disimpan untuk ejen ini.",
|
||||
"ui.agent_chat_models_failed": "Tidak dapat menyenaraikan model: {error}",
|
||||
"ui.agent_chat_models_heading": "Model yang tersedia",
|
||||
"ui.agent_chat_models_listing": "Menyenaraikan model…",
|
||||
"ui.agent_chat_models_none": "Titik akhir tidak menyenaraikan sebarang model",
|
||||
"ui.agent_chat_models_not_a_list": "Jawapan itu bukan senarai model",
|
||||
"ui.agent_chat_new": "Sembang baharu",
|
||||
"ui.agent_chat_new_confirm": "Mulakan sembang baharu? Sembang semasa akan dikosongkan.",
|
||||
"ui.agent_chat_next_picture": "Gambar seterusnya",
|
||||
@@ -4407,14 +4474,21 @@
|
||||
"ui.construction.contact_hint": "Tangen: pilih bulatan/lengkok atau splin/lengkung kon terbuka, kemudian titik sentuh (lengkung bentuk bebas: titik hujung)",
|
||||
"ui.construction.external_hint": "Tangen: pilih bulatan atau lengkok, satu titik luar, kemudian penyelesaian yang dikehendaki",
|
||||
"ui.construction.free_hint": "Garis binaan: klik dua titik; klik kanan untuk mula semula",
|
||||
"ui.construction.normal_needs_length": "Garis tak terhingga sentiasa garis binaan: taip panjang untuk melukis garis biasa",
|
||||
"ui.construction.point_hint": "Garis binaan: klik titik yang akan dilalui",
|
||||
"ui.construction.reference_hint": "Garis binaan: pilih garis lurus, kemudian satu titik; klik kanan untuk menukar rujukan",
|
||||
"ui.construction.ribbon_label": "Garis bantu",
|
||||
"ui.construction.side_hint": "Garis binaan: klik sisi yang dituju garis; klik kanan untuk mula semula",
|
||||
"ui.context_menu": "Menu konteks",
|
||||
"ui.continuity_crease": "Lipatan",
|
||||
"ui.continuity_curvature": "Kelengkungan",
|
||||
"ui.continuity_curvature_change": "Perubahan kelengkungan",
|
||||
"ui.continuity_g0": "Lipatan G0",
|
||||
"ui.continuity_g1": "Tangen G1",
|
||||
"ui.continuity_g2": "Kelengkungan G2",
|
||||
"ui.continuity_gap": "Sela",
|
||||
"ui.continuity_position": "Kedudukan",
|
||||
"ui.continuity_tangent": "Tangen",
|
||||
"ui.copy": "Salin",
|
||||
"ui.corner": "Sudut",
|
||||
"ui.corners": "Sudut",
|
||||
@@ -4675,6 +4749,7 @@
|
||||
"ui.hideOrigin": "Sembunyi Titik Asal",
|
||||
"ui.hidePlanes": "Sembunyi Satah",
|
||||
"ui.hideProfile": "Sembunyi Profil",
|
||||
"ui.hide_component_history": "Sembunyikan Sejarahnya",
|
||||
"ui.hide_origin_by_default": "Sembunyi titik asal secara lalai",
|
||||
"ui.hide_origin_note": "Reka bentuk baharu bermula dengan folder Titik Asal disembunyikan. Sesuatu reka bentuk mengekalkan tetapan Titik Asalnya sendiri sebaik sahaja anda memaparkan atau menyembunyikannya.",
|
||||
"ui.history_back": "Undur satu langkah",
|
||||
@@ -4682,6 +4757,7 @@
|
||||
"ui.history_edge_hint": "klik dua kali pada tepi",
|
||||
"ui.history_first": "Pergi ke permulaan",
|
||||
"ui.history_forward": "Maju satu langkah",
|
||||
"ui.history_hide_hidden": "Sembunyikan Komponen Tersembunyi",
|
||||
"ui.history_hide_names": "Sembunyi nama",
|
||||
"ui.history_last": "Pergi ke penghujung",
|
||||
"ui.history_marker_tip": "Penanda sejarah: seret untuk mengundurkan model; ciri baharu diletakkan di sini",
|
||||
@@ -4691,10 +4767,12 @@
|
||||
"ui.history_play_button_show": "Papar Butang Main",
|
||||
"ui.history_play_finish_action": "Selesaikan tindakan semasa sebelum memainkan Sejarah",
|
||||
"ui.history_play_roll_back": "Alih penanda Sejarah ke belakang sebelum memainkan",
|
||||
"ui.history_show_hidden": "Papar Komponen Tersembunyi",
|
||||
"ui.history_show_names": "Papar nama",
|
||||
"ui.history_sketch_listed_under": "Disenaraikan di bawah {feature}",
|
||||
"ui.history_title": "Sejarah",
|
||||
"ui.history_toggle_names": "Papar atau sembunyikan nama ciri",
|
||||
"ui.history_waits_for_editor": "Buat Asal, Buat Semula dan Sejarah menunggu sehingga penyunting ciri dibuka",
|
||||
"ui.hole": "Lubang",
|
||||
"ui.hole_tap_type": "Jenis Bebenang Lubang",
|
||||
"ui.hole_type": "Jenis Lubang",
|
||||
@@ -4710,6 +4788,7 @@
|
||||
"ui.insert_design.saved_file": "{name} mempunyai perubahan yang belum disimpan, jadi fail tersimpannya disisipkan. Simpan reka bentuk itu, kemudian gunakan Kemas Kini Pautan untuk membawa masuk perubahan.",
|
||||
"ui.insert_design.unsaved": "Perubahan belum disimpan",
|
||||
"ui.inspect": "Periksa",
|
||||
"ui.instances": "Salinan",
|
||||
"ui.intersect": "Silang",
|
||||
"ui.isolate": "Asingkan",
|
||||
"ui.join": "Cantum",
|
||||
@@ -4974,6 +5053,7 @@
|
||||
"ui.showDimension": "Papar Dimensi",
|
||||
"ui.showOrigin": "Papar Titik Asal",
|
||||
"ui.showPlanes": "Papar Satah",
|
||||
"ui.show_component_history": "Papar Sejarahnya",
|
||||
"ui.show_constraints": "Kekangan",
|
||||
"ui.show_construction_geometries": "Geometri Binaan",
|
||||
"ui.show_dimensions": "Dimensi",
|
||||
@@ -5156,7 +5236,7 @@
|
||||
"update.check_unavailable": "Semakan kemas kini tidak tersedia untuk pemasangan ini.",
|
||||
"update.checking": "Menyemak…",
|
||||
"update.copy": "Salin",
|
||||
"update.dmg_opened": "Imej cakera telah dibuka dan disahkan: seret SolidCraft ke Applications untuk menggantikan salinan lama, kemudian mulakan semula SolidCraft.",
|
||||
"update.dmg_opened": "Imej cakera telah dibuka dan disahkan: keluar dari SolidCraft, seret SolidCraft yang baharu ke Applications untuk menggantikan salinan lama, kemudian buka SolidCraft semula.",
|
||||
"update.download": "Muat turun",
|
||||
"update.download_open": "Muat turun dan buka",
|
||||
"update.downloading": "Memuat turun dan mengesahkan… {done} daripada {total} MB",
|
||||
@@ -5200,6 +5280,7 @@
|
||||
"web.open_link": "Buka reka bentuk yang dipautkan?",
|
||||
"web.open_link_confirm": "Terdapat perubahan yang belum disimpan. Buka reka bentuk yang dipautkan dalam tab baharu?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ pilih · Enter / Tab lengkapkan · Esc tutup",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ pilih · Enter menggunakan nilai ini",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ pilih · Enter menggunakan apa yang anda taip · Esc tutup",
|
||||
"⌘Enter continues on GitHub.": "⌘Enter diteruskan di GitHub."
|
||||
}
|
||||
|
||||
+85
-4
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "Rektangulær",
|
||||
"Rectangular Tube": "Rektangulært rør",
|
||||
"Reference face": "Referanseflate",
|
||||
"Reference plane": "Referanseplan",
|
||||
"Relief Depth": "Avlastningsdybde",
|
||||
"Relief Width": "Avlastningsbredde",
|
||||
"Remove Tools": "Fjern verktøy",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "Snitt",
|
||||
"Section (optional)": "Tverrsnitt (valgfritt)",
|
||||
"Section position": "Snittposisjon",
|
||||
"Sections": "Tverrsnitt",
|
||||
"Sections (one sketch each)": "Tverrsnitt (én skisse hver)",
|
||||
"Select Other": "Velg annet",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Velg annet: Hold venstre museknapp inne uten å flytte pekeren på modellen (eller høyreklikk › Velg annet…) for å vise alt under markøren. Klikk på samme sted igjen for å velge neste objekt. Dobbeltklikk med midtknappen for å gå til hjemvisningen (Innstillinger › Navigasjon).",
|
||||
@@ -859,6 +861,7 @@
|
||||
"cmd.component_extract": "Trekk ut til fil",
|
||||
"cmd.component_finish_edit": "Fullfør redigering",
|
||||
"cmd.component_from_bodies": "Opprett komponenter fra kropper",
|
||||
"cmd.component_hide_history": "Skjul komponentens tidslinje",
|
||||
"cmd.component_insert_design": "Sett inn design",
|
||||
"cmd.component_links": "Vis koblinger",
|
||||
"cmd.component_list": "Vis komponenter",
|
||||
@@ -875,6 +878,7 @@
|
||||
"cmd.component_rename": "Gi nytt navn til komponent",
|
||||
"cmd.component_replace_with_link": "Erstatt med lenke",
|
||||
"cmd.component_revert_position": "Tilbakestill posisjon",
|
||||
"cmd.component_show_history": "Vis komponentens tidslinje",
|
||||
"cmd.component_unlock": "Lås opp innhold",
|
||||
"cmd.component_update_links": "Oppdater koblinger",
|
||||
"cmd.config_activate": "Aktiver konfigurasjon",
|
||||
@@ -885,6 +889,7 @@
|
||||
"cmd.config_rule": "Konfigurasjonsregel",
|
||||
"cmd.config_rules": "Konfigurasjonsregler",
|
||||
"cmd.config_table": "Vis konfigurasjonstabell",
|
||||
"cmd.construct.plane.axis_point": "Plan gjennom akse og punkt",
|
||||
"cmd.construct_axis_edge": "Akse gjennom kant",
|
||||
"cmd.construct_axis_normal_to_face": "Akse vinkelrett på flate",
|
||||
"cmd.construct_axis_through_cylinder": "Akse gjennom sylinder/kjegle/torus",
|
||||
@@ -933,6 +938,11 @@
|
||||
"cmd.explode_show": "Vis eksplodert visning",
|
||||
"cmd.expr_evaluate": "Evaluer uttrykk",
|
||||
"cmd.face_delete": "Slett flate",
|
||||
"cmd.file.export_3mf": "Eksporter 3MF",
|
||||
"cmd.file.export_obj": "Eksporter OBJ",
|
||||
"cmd.file.export_step": "Eksporter STEP",
|
||||
"cmd.file.export_stl": "Eksporter STL",
|
||||
"cmd.file.export_urdf": "Eksporter URDF",
|
||||
"cmd.file_export": "Eksporter",
|
||||
"cmd.file_insert_mesh": "Sett inn mesh",
|
||||
"cmd.file_insert_step": "Sett inn STEP",
|
||||
@@ -950,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "Aktiver komponentfarger",
|
||||
"cmd.inspect_colored_components_disable": "Deaktiver komponentfarger",
|
||||
"cmd.inspect_compare": "Sammenlign kropper",
|
||||
"cmd.inspect_compare_picture": "Sammenlign bilde",
|
||||
"cmd.inspect_continuity": "Kontinuitetsanalyse",
|
||||
"cmd.inspect_curvature_comb": "Analyse med krumningskam",
|
||||
"cmd.inspect_curvature_map": "Analyse med krumningskart",
|
||||
@@ -1040,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "Rediger punktsky",
|
||||
"cmd.pointcloud_insert": "Sett inn punktsky",
|
||||
"cmd.pointcloud_list": "Punktskyer",
|
||||
"cmd.render.compare.divider": "Flytt skillelinjen i rendringssammenligningen",
|
||||
"cmd.render.result.discard": "Forkast rendringsresultat",
|
||||
"cmd.render.result.select": "Velg rendringsresultat",
|
||||
"cmd.render_aging_describe": "Lagdelt overflatealdring",
|
||||
"cmd.render_backends": "Rendringsmotorer",
|
||||
"cmd.render_batch": "Rendre alle opptak",
|
||||
@@ -1254,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "Flytt tidslinjemarkør",
|
||||
"cmd.timeline_stop": "Stopp avspilling av tidslinje",
|
||||
"cmd.timeline_suppress": "Undertrykk funksjon",
|
||||
"cmd.ui.assistant": "Vis/skjul assistent",
|
||||
"cmd.view.render_browser": "Rendringsutforsker",
|
||||
"cmd.view.render_dock": "Flytt rendringspanel",
|
||||
"cmd.view.render_focus": "Rendringsfokusvisning",
|
||||
@@ -1288,9 +1303,11 @@
|
||||
"cmd.view_tool": "Navigasjonsverktøy",
|
||||
"cmd.view_top": "Toppvisning",
|
||||
"cmd.view_visual_style": "Visuell stil",
|
||||
"color.blue": "Blå",
|
||||
"color.copy": "Klikk for å kopiere fargeverdier",
|
||||
"color.edit": "Klikk for å redigere farge",
|
||||
"color.gamma": "Viser gammakorrigerte fargeverdier i området 0–255",
|
||||
"color.green": "Grønn",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "Skriv inn #rrggbb eller #rrggbbaa; seks sifre beholder opasiteten",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1298,6 +1315,7 @@
|
||||
"color.linear": "Viser lineære fargeverdier i området 0–1",
|
||||
"color.recent": "Nylige farger",
|
||||
"color.recent_help": "Fargene du velger i et fargefelt, vises her",
|
||||
"color.red": "Rød",
|
||||
"color.saturation": "Metning (prosent)",
|
||||
"color.saturation_value": "Metning og lysverdi",
|
||||
"color.selected": "Valgt farge",
|
||||
@@ -1305,6 +1323,8 @@
|
||||
"color.step_up": "Ett trinn opp",
|
||||
"color.value": "Lysverdi, lysstyrken (prosent)",
|
||||
"config.column_filter_empty": "Ingen samsvarende kolonner",
|
||||
"config.rule_parameter_columns": "Bare parametere som har en kolonne i tabellen, er med i listen. Legg til en kolonne med Legg til kolonne (+) for å bruke parameteren her.",
|
||||
"config.rule_suppression_columns": "Bare funksjoner som har en Undertrykk-kolonne i tabellen, er med i listen. Legg til en kolonne med Legg til kolonne (+) for å styre undertrykkingen her.",
|
||||
"contact.components_count": "{count} komponenter",
|
||||
"contact.enabled_label": "aktivert",
|
||||
"coupling.gear_1": "Tannhjul 1",
|
||||
@@ -1353,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "Velg kropper eller funksjoner: en skisse er ingen av delene. Gjenta kurvene med skissens eget Mønster eller Speil, eller ekstruder den først.",
|
||||
"dialog.select": "↖ Velg",
|
||||
"dialog.selected_count": "↖ {count} valgt",
|
||||
"dialog.selection_constraint": "Binding {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · kurve {id}",
|
||||
"dialog.selection_guidance": "Velg et felt for å endre det. Klikk på valgte objekter for å fjerne dem fra utvalget; Ctrl/Cmd+klikk veksler også mellom valgt og ikke valgt. Se over forhåndsvisningen, og trykk deretter OK.",
|
||||
"dialog.selection_midpoint": "Midtpunkt · {id}",
|
||||
"dialog.selection_profile": "{sketch} · profil {number}",
|
||||
"dialog.selection_step": "{step} av {total}: Velg {input}",
|
||||
"dialog.step": "Trinn",
|
||||
"document.changed_on_disk": "{name} er endret på disken",
|
||||
@@ -1436,6 +1460,7 @@
|
||||
"help.construct.axis.two_points": "Oppretter en hjelpeakse gjennom to punkter, hjørner eller skissepunkter. Bruk den til en akse mellom to kjente posisjoner.",
|
||||
"help.construct.plane.along_path": "Oppretter et hjelpeplan vinkelrett på en kant eller kurve, på en posisjon langs den. Bruk det til å skisse en profil for en sweep eller et snitt av en bane.",
|
||||
"help.construct.plane.at_angle": "Oppretter et hjelpeplan vridd med en vinkel fra et plan eller en flate, om en akse eller kant. Bruk det til skråstilte skisser, for eksempel en vinklet brakett.",
|
||||
"help.construct.plane.axis_point": "Oppretter et hjelpeplan som inneholder en akse og et punkt utenfor aksen. Velg en origoakse eller hjelpeakse, en rett kant eller skisselinje, og deretter et hjørne, skissepunkt eller hjelpepunkt. Et punkt på aksen kan ikke definere et entydig plan.",
|
||||
"help.construct.plane.midplane": "Oppretter et hjelpeplan midt mellom to plan eller plane flater, eller som halverer dem der de møtes i en vinkel. Bruk det som symmetriplan for speiling.",
|
||||
"help.construct.plane.offset": "Oppretter et hjelpeplan parallelt med et plan eller en plan flate, i en avstand eller gjennom et punkt. Bruk det til å skisse i en bestemt høyde over en flate.",
|
||||
"help.construct.plane.perpendicular": "Oppretter et hjelpeplan som inneholder en linje eller kant og står vinkelrett på et plan eller en flate. Bruk det til å skisse oppreist på en modell langs en kant.",
|
||||
@@ -1812,6 +1837,8 @@
|
||||
"joint_move.blocked_hint": "Kontroller leddtypen (for eksempel Stiv), om posisjonen er festet, leddgrenser og parameterstyrte verdier.",
|
||||
"joint_move.blocked_title": "Kan ikke flytte denne delen",
|
||||
"joint_move.coarse": "Rotasjonstrinn nær aksen (°)",
|
||||
"joint_move.conflict": "Et ledd på denne delen er i konflikt med de andre: {joint}.",
|
||||
"joint_move.conflict_hint": "Rediger, undertrykk eller slett det konfliktende leddet, slik at de andre leddene kan plassere delen.",
|
||||
"joint_move.distance": "Avstand fra start",
|
||||
"joint_move.done": "Ferdig",
|
||||
"joint_move.fine": "Rotasjonstrinn langt fra aksen (°)",
|
||||
@@ -1911,6 +1938,8 @@
|
||||
"motion.track_name": "Spor {number}",
|
||||
"nav.alias.help": "Shift+Alt+venstredrag roterer, Shift+Alt+midtdrag panorerer, Shift+Alt+høyredrag zoomer; midtdrag panorerer og høyredrag roterer også",
|
||||
"nav.blender.help": "Midtdrag roterer, Shift+midtdrag panorerer, Ctrl+midtdrag zoomer; høyredrag lar kameraet stå i ro",
|
||||
"nav.creo.help": "Midtdrag roterer, Shift+midtdrag panorerer, Ctrl+midtdrag vertikalt zoomer (ned for å zoome inn); rulling med hjulet zoomer",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "Mus og 3D-mus roterer gjennom begge polene og bevarer rulling. Aksefangst med Shift er deaktivert.",
|
||||
"nav.free_orbit.label": "Fri rotasjon",
|
||||
"nav.free_orbit.snap_help": "Mus og 3D-mus roterer gjennom begge polene og bevarer rull. Hold Shift mens du drar for å rotere, så låses visningsretningen og rullen til faste trinn.",
|
||||
@@ -2063,12 +2092,19 @@
|
||||
"parts.family.button_head_cap_screw": "Linsehodeskrue med innvendig sekskant",
|
||||
"parts.family.dowel_pin": "Parallellstift (passtift)",
|
||||
"parts.family.hex_bolt": "Sekskantbolt / sekskantskrue",
|
||||
"parts.family.hex_cap_screw_inch": "Sekskantskrue (tommer)",
|
||||
"parts.family.hex_nut": "Sekskantmutter",
|
||||
"parts.family.hex_nut_inch": "Sekskantmutter (tommer)",
|
||||
"parts.family.set_screw": "Settskrue med innvendig sekskant, flat ende",
|
||||
"parts.family.socket_head_cap_screw": "Sylinderskrue med innvendig sekskant",
|
||||
"parts.family.washer": "Underlagsskive",
|
||||
"parts.family.washer_sae": "Underlagsskive, SAE (smal)",
|
||||
"parts.family.washer_uss": "Underlagsskive, USS (bred)",
|
||||
"parts.holes": "Hull (valgfritt)",
|
||||
"parts.placement_hint": "velg ett eller flere hull for å sette inn en del i hvert av dem, eller OK plasserer den ved origo",
|
||||
"parts.system": "Enhetssystem",
|
||||
"parts.system.inch": "Tommer",
|
||||
"parts.system.metric": "Metrisk",
|
||||
"pattern.alignment_2": "Justering 2",
|
||||
"pattern.alignment_hint": "Antallet inkluderer originalen. Senter fordeler kopiene rundt originalen. Speil velger hvilken side som får en ekstra kopi når antallet er et partall.",
|
||||
"pattern.direction_1": "Retning 1",
|
||||
@@ -2130,6 +2166,8 @@
|
||||
"prefs.button_left": "Dra med venstre museknapp",
|
||||
"prefs.button_middle": "Dra med midtre museknapp",
|
||||
"prefs.button_right": "Dra med høyre museknapp",
|
||||
"prefs.carousel_next": "Neste",
|
||||
"prefs.carousel_previous": "Forrige",
|
||||
"prefs.choose_font": "Velg skriftfil…",
|
||||
"prefs.click": "Klikk",
|
||||
"prefs.contact_shading": "Myk kontaktskyggelegging",
|
||||
@@ -2142,6 +2180,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Buede flater",
|
||||
"prefs.curves": "Buede flater",
|
||||
"prefs.cut_preview_tint": "Vis fjernet materiale i rødt",
|
||||
"prefs.cut_preview_tint_help": "En forhåndsvisning fyller materialet en funksjon fjerner, og formen den erstatter, med et rødt, gjennomsiktig spøkelse. Når dette er av, er fyllene helt gjennomsiktige, og bare konturene tegnes.",
|
||||
"prefs.dark_parts_shading": "Bevar formen på mørke deler",
|
||||
"prefs.dark_parts_shading_help": "Skygger visningen i lineært lys med en myk himmelfylling, slik at mørke flater beholder en gradient i stedet for å bli svarte",
|
||||
"prefs.dead_zone_note": "Det skraverte feltet er dødsonen: bevegelser innenfor den ignoreres.",
|
||||
@@ -2200,6 +2240,8 @@
|
||||
"prefs.interface_language": "Grensesnittspråk",
|
||||
"prefs.interface_note": "Hvordan SolidCraft ser ut, og hvor delene er plassert.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_curved_roll_arrows": "Inverter de krumme rullepilene",
|
||||
"prefs.invert_curved_roll_arrows_note": "De to krumme pilene i hjørnet av kuben ruller visningen i skjermplanet. På: pilen med klokken ruller mot klokken, og pilen mot klokken ruller med klokken. Av: hver pil ruller visningen i den retningen den krummer seg.",
|
||||
"prefs.invert_navigator_arrows": "Inverter rotasjonspilene på visningskuben",
|
||||
"prefs.invert_navigator_arrows_note": "Pilene rundt kuben roterer kameraet i den retningen de peker, som i Fusion og Inventor: pilen til venstre peker mot høyre og svinger kameraet mot høyre. Av: en pil roterer kuben mot siden den står på, så venstre pil svinger kameraet mot venstre. De krumme rullepilene påvirkes ikke.",
|
||||
"prefs.keep_work_on_tab_switch": "Behold dialoger og verktøy åpne når du bytter design",
|
||||
@@ -2259,6 +2301,8 @@
|
||||
"prefs.placement": "Plassering",
|
||||
"prefs.prefer_free_sketch_points": "Foretrekk flyttbare skissepunkter når punkter overlapper",
|
||||
"prefs.preview_choices": "Forhåndsvisning av valgene dine",
|
||||
"prefs.preview_tint_new_faces": "Fargelegg ny overflate i en forhåndsvisning",
|
||||
"prefs.preview_tint_new_faces_help": "En forhåndsvisning av en funksjon som endrer en kropp, kan tegne overflaten den legger til i forhåndsvisningsfargen. Når dette er av, tegnes kroppen som seg selv.",
|
||||
"prefs.print_face_highlight": "Utheving av utskriftsflate",
|
||||
"prefs.print_face_highlight_tip": "Hvor sterkt et legemes utskriftsflate fargelegges og omrisses i visningen. 0 % viser ingen utheving.",
|
||||
"prefs.profile_active": "Aktiv profil",
|
||||
@@ -2556,6 +2600,10 @@
|
||||
"render.base_color": "Grunnfarge · sRGB",
|
||||
"render.base_metallic": "Metallfraksjon",
|
||||
"render.base_opaque_only": "Denne redigeringen bevarer semantikken for ugjennomsiktige kropper. Bruk de eksplisitte glasskontrollene for optisk transmisjon.",
|
||||
"render.batch_all_shots": "Alle opptak ({count})",
|
||||
"render.batch_empty": "Lagre et opptak før alle opptak rendres.",
|
||||
"render.batch_limit": "Å rendre alle opptak er begrenset til 32 opptak.",
|
||||
"render.batch_status": "{running} pågår · {waiting} venter",
|
||||
"render.bloom_enable": "Vis bloom",
|
||||
"render.bloom_help": "Kreativ glød kun i utdata, fra positive lineære høylys, spredt med tre normaliserte boksfilterpasseringer. Radius er i utdatapiksler. Legges til i lineært visningslys før sRGB; ikke en linse- eller volumeffekt eller en forbedring av konvergens. Rå EXR er uendret.",
|
||||
"render.bloom_opaque": "Bloom krever ugjennomsiktig utdata. Gjennomsiktig PNG avvises og flates ikke ut i det stille.",
|
||||
@@ -2666,6 +2714,13 @@
|
||||
"render.choose_look": "Velg en stil i Utseender først",
|
||||
"render.chromatic_offset": "Kromatisk kantforskyvning",
|
||||
"render.clear_stage": "Fjern lagret rendringsdekor",
|
||||
"render.compare_divider": "Skillelinje i sammenligningen",
|
||||
"render.compare_live": "Live",
|
||||
"render.compare_live_preview": "Live-forhåndsvisning",
|
||||
"render.compare_render": "Rendring {number}",
|
||||
"render.compare_side": "Rendring {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} s · {renderer} · registrert ved revisjon {revision}",
|
||||
"render.compare_swap": "Bytt plass",
|
||||
"render.copy_request": "Kopier JSON",
|
||||
"render.cpu_aa_help": "Romlige prøver: flere prøver forbedrer kanter og øker rendringstiden.",
|
||||
"render.cpu_area_add": "Legg til rektangulært CPU-lys",
|
||||
@@ -3415,6 +3470,10 @@
|
||||
"render.result_retained": "Viser det siste ferdige bildet; en nyere rendring har ikke erstattet det.",
|
||||
"render.result_sequence": "Sjekkpunkt",
|
||||
"render.result_title": "Rendringsresultat",
|
||||
"render.results_choose": "Beholdte resultater",
|
||||
"render.results_label": "Rendring {number} · {shot} · {seconds} s",
|
||||
"render.results_stale_label": "{result} [utdatert]",
|
||||
"render.results_unnamed": "Bilde",
|
||||
"render.return_design": "Tilbake til design",
|
||||
"render.review_stale": "Denne gjennomgangen er utdatert. Avklar eller sjekk på nytt før du bruker den.",
|
||||
"render.ribbon_aging": "Aldring",
|
||||
@@ -3898,10 +3957,7 @@
|
||||
"the two origins are on the same component": "de to origoene ligger på samme komponent",
|
||||
"thread.automatic_size": "Automatisk størrelse",
|
||||
"thread.designation": "Betegnelse",
|
||||
"thread.direction": "Gjengeretning",
|
||||
"thread.iso_metric": "ISO-metrisk",
|
||||
"thread.left_hand": "Venstregjenget",
|
||||
"thread.right_hand": "Høyregjenget",
|
||||
"thread.select_size": "Velg størrelse",
|
||||
"timeline.delete_after_marker_one": "Slett 1 funksjon etter historikkmarkøren",
|
||||
"timeline.delete_after_marker_other": "Slett {count} funksjoner etter historikkmarkøren",
|
||||
@@ -4075,6 +4131,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Kjør dette i en terminal, og trykk deretter Koble til igjen:",
|
||||
"ui.agent_chat_auth_title": "Logg inn på agenten",
|
||||
"ui.agent_chat_cancel": "Avbryt",
|
||||
"ui.agent_chat_cancel_timeout": "Agenten svarte ikke på Stopp og ble koblet fra. Koble til igjen for å fortsette.",
|
||||
"ui.agent_chat_cannot_start": "Installert, men starter ikke: {error}",
|
||||
"ui.agent_chat_changes": "Endringer: {n}, ett angretrinn",
|
||||
"ui.agent_chat_changes_n": "{n} endringer",
|
||||
@@ -4084,11 +4141,13 @@
|
||||
"ui.agent_chat_command": "Agentkommando",
|
||||
"ui.agent_chat_connect": "Koble til",
|
||||
"ui.agent_chat_copy": "Kopier",
|
||||
"ui.agent_chat_copy_message": "Kopier melding",
|
||||
"ui.agent_chat_ctx_bodies": "Kropper",
|
||||
"ui.agent_chat_ctx_elements": "Elementer",
|
||||
"ui.agent_chat_ctx_features": "Funksjoner",
|
||||
"ui.agent_chat_ctx_more": "+{n} til",
|
||||
"ui.agent_chat_ctx_other": "Annet",
|
||||
"ui.agent_chat_custom_heading": "Egendefinert modell",
|
||||
"ui.agent_chat_custom_hint": "Et program som bruker Agent Client Protocol via stdin og stdout. Programmet startes uten et kommandoskall.",
|
||||
"ui.agent_chat_denied": "Avvist",
|
||||
"ui.agent_chat_deny": "Avvis",
|
||||
@@ -4132,8 +4191,16 @@
|
||||
"ui.agent_chat_mode_all": "Alle",
|
||||
"ui.agent_chat_mode_ask": "Spør",
|
||||
"ui.agent_chat_mode_edits": "Endringer",
|
||||
"ui.agent_chat_model_custom": "Bruk egendefinert modell: {model}",
|
||||
"ui.agent_chat_model_no_tools": "{model} kan ikke bruke verktøyene i SolidCraft, så den kan bare chatte. Velg en modell med verktøystøtte.",
|
||||
"ui.agent_chat_model_rejected": "Agenten godtok ikke {model}: {error}",
|
||||
"ui.agent_chat_model_search": "Søk etter modeller…",
|
||||
"ui.agent_chat_model_tip": "Modellen agenten bruker. Valget ditt huskes for denne agenten.",
|
||||
"ui.agent_chat_models_failed": "Kunne ikke liste opp modellene: {error}",
|
||||
"ui.agent_chat_models_heading": "Tilgjengelige modeller",
|
||||
"ui.agent_chat_models_listing": "Henter modeller…",
|
||||
"ui.agent_chat_models_none": "Endepunktet viser ingen modeller",
|
||||
"ui.agent_chat_models_not_a_list": "Svaret var ikke en modellliste",
|
||||
"ui.agent_chat_new": "Ny chat",
|
||||
"ui.agent_chat_new_confirm": "Starte en ny chat? Den nåværende tømmes.",
|
||||
"ui.agent_chat_next_picture": "Neste bilde",
|
||||
@@ -4407,14 +4474,21 @@
|
||||
"ui.construction.contact_hint": "Tangent: velg en sirkel/bue eller åpen spline/kjeglesnittkurve, og deretter berøringspunktet (frie kurver: endepunkter)",
|
||||
"ui.construction.external_hint": "Tangent: velg en sirkel eller bue, et eksternt punkt, og deretter ønsket løsning",
|
||||
"ui.construction.free_hint": "Hjelpelinje: klikk på to punkter; høyreklikk for å starte på nytt",
|
||||
"ui.construction.normal_needs_length": "En uendelig linje er alltid en hjelpelinje: skriv inn en lengde for å tegne en normal linje",
|
||||
"ui.construction.point_hint": "Hjelpelinje: klikk på punktet linjen skal gå gjennom",
|
||||
"ui.construction.reference_hint": "Hjelpelinje: velg en rett linje, og deretter et punkt; høyreklikk for å bytte referanse",
|
||||
"ui.construction.ribbon_label": "Hjelpelinjer",
|
||||
"ui.construction.side_hint": "Hjelpelinje: klikk på siden linjen går mot; høyreklikk for å starte på nytt",
|
||||
"ui.context_menu": "Kontekstmeny",
|
||||
"ui.continuity_crease": "Knekk",
|
||||
"ui.continuity_curvature": "Krumning",
|
||||
"ui.continuity_curvature_change": "Krumningsendring",
|
||||
"ui.continuity_g0": "G0-knekk",
|
||||
"ui.continuity_g1": "G1-tangent",
|
||||
"ui.continuity_g2": "G2-krumning",
|
||||
"ui.continuity_gap": "Gap",
|
||||
"ui.continuity_position": "Posisjon",
|
||||
"ui.continuity_tangent": "Tangent",
|
||||
"ui.copy": "Kopier",
|
||||
"ui.corner": "Hjørne",
|
||||
"ui.corners": "Hjørner",
|
||||
@@ -4675,6 +4749,7 @@
|
||||
"ui.hideOrigin": "Skjul origo",
|
||||
"ui.hidePlanes": "Skjul plan",
|
||||
"ui.hideProfile": "Skjul profil",
|
||||
"ui.hide_component_history": "Skjul dens tidslinje",
|
||||
"ui.hide_origin_by_default": "Skjul origo som standard",
|
||||
"ui.hide_origin_note": "Nye design starter med Origo-mappen skjult. Et design beholder sin egen Origo-innstilling når du har vist eller skjult den.",
|
||||
"ui.history_back": "Gå ett trinn tilbake",
|
||||
@@ -4682,6 +4757,7 @@
|
||||
"ui.history_edge_hint": "dobbeltklikk kanten",
|
||||
"ui.history_first": "Gå til starten",
|
||||
"ui.history_forward": "Gå ett trinn frem",
|
||||
"ui.history_hide_hidden": "Skjul skjulte komponenter",
|
||||
"ui.history_hide_names": "Skjul navn",
|
||||
"ui.history_last": "Gå til slutten",
|
||||
"ui.history_marker_tip": "Tidslinjemarkør: dra for å vise en tidligere tilstand av modellen. Nye funksjoner legges til her.",
|
||||
@@ -4691,10 +4767,12 @@
|
||||
"ui.history_play_button_show": "Vis avspillingsknapp",
|
||||
"ui.history_play_finish_action": "Fullfør gjeldende handling før du spiller av tidslinjen",
|
||||
"ui.history_play_roll_back": "Flytt tidslinjemarkøren tilbake før avspilling",
|
||||
"ui.history_show_hidden": "Vis skjulte komponenter",
|
||||
"ui.history_show_names": "Vis navn",
|
||||
"ui.history_sketch_listed_under": "Oppført under {feature}",
|
||||
"ui.history_title": "Tidslinje",
|
||||
"ui.history_toggle_names": "Vis eller skjul funksjonsnavn",
|
||||
"ui.history_waits_for_editor": "Angre, Gjør om og Tidslinje venter til funksjonsredigeringen er åpnet",
|
||||
"ui.hole": "Hull",
|
||||
"ui.hole_tap_type": "Type gjenging",
|
||||
"ui.hole_type": "Hulltype",
|
||||
@@ -4710,6 +4788,7 @@
|
||||
"ui.insert_design.saved_file": "{name} har ulagrede endringer, så den lagrede filen ble satt inn. Lagre den, og bruk deretter Oppdater koblinger for å hente inn endringene.",
|
||||
"ui.insert_design.unsaved": "Ulagrede endringer",
|
||||
"ui.inspect": "Inspiser",
|
||||
"ui.instances": "Forekomster",
|
||||
"ui.intersect": "Snitt",
|
||||
"ui.isolate": "Isoler",
|
||||
"ui.join": "Slå sammen",
|
||||
@@ -4974,6 +5053,7 @@
|
||||
"ui.showDimension": "Vis mål",
|
||||
"ui.showOrigin": "Vis origo",
|
||||
"ui.showPlanes": "Vis plan",
|
||||
"ui.show_component_history": "Vis dens tidslinje",
|
||||
"ui.show_constraints": "Bindinger",
|
||||
"ui.show_construction_geometries": "Konstruksjonsgeometri",
|
||||
"ui.show_dimensions": "Mål",
|
||||
@@ -5156,7 +5236,7 @@
|
||||
"update.check_unavailable": "Søk etter oppdateringer er ikke tilgjengelig for denne installasjonen.",
|
||||
"update.checking": "Sjekker…",
|
||||
"update.copy": "Kopier",
|
||||
"update.dmg_opened": "Diskbildet er åpent og verifisert: dra SolidCraft til Applications, erstatt den gamle kopien, og start deretter SolidCraft på nytt.",
|
||||
"update.dmg_opened": "Diskbildet er åpent og verifisert: avslutt SolidCraft, dra det nye SolidCraft til Applications, erstatt den gamle kopien, og åpne deretter SolidCraft igjen.",
|
||||
"update.download": "Last ned",
|
||||
"update.download_open": "Last ned og åpne",
|
||||
"update.downloading": "Laster ned og verifiserer… {done} av {total} MB",
|
||||
@@ -5200,6 +5280,7 @@
|
||||
"web.open_link": "Åpne lenket design?",
|
||||
"web.open_link_confirm": "Det finnes ulagrede endringer. Åpne det lenkede designet i en ny fane?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ velg · Enter / Tab fullfør · Esc lukk",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ velg · Enter bruker denne verdien",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ velg · Enter bruker det du skrev · Esc lukk",
|
||||
"⌘Enter continues on GitHub.": "⌘Enter fortsetter på GitHub."
|
||||
}
|
||||
|
||||
+84
-4
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "Rechthoekig",
|
||||
"Rectangular Tube": "Rechthoekige buis",
|
||||
"Reference face": "Referentievlak",
|
||||
"Reference plane": "Referentievlak",
|
||||
"Relief Depth": "Vrijsnijdiepte",
|
||||
"Relief Width": "Vrijsnijbreedte",
|
||||
"Remove Tools": "Gereedschappen verwijderen",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "Doorsnede",
|
||||
"Section (optional)": "Doorsnede (optioneel)",
|
||||
"Section position": "Positie van de doorsnede",
|
||||
"Sections": "Doorsneden",
|
||||
"Sections (one sketch each)": "Doorsneden (één schets per doorsnede)",
|
||||
"Select Other": "Overige selecteren",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Overige selecteren: houd de linkermuisknop stil op het model (of klik met rechts › Overige selecteren…) om alles onder de cursor weer te geven; klik opnieuw op dezelfde plek om het volgende item daar te selecteren. Dubbelklik met de middelste knop om naar Home te gaan (Voorkeuren › Navigatie).",
|
||||
@@ -859,6 +861,7 @@
|
||||
"cmd.component_extract": "Naar bestand extraheren",
|
||||
"cmd.component_finish_edit": "Bewerken voltooien",
|
||||
"cmd.component_from_bodies": "Componenten maken van lichamen",
|
||||
"cmd.component_hide_history": "Componentgeschiedenis verbergen",
|
||||
"cmd.component_insert_design": "Ontwerp invoegen",
|
||||
"cmd.component_links": "Koppelingen weergeven",
|
||||
"cmd.component_list": "Componenten weergeven",
|
||||
@@ -875,6 +878,7 @@
|
||||
"cmd.component_rename": "Component hernoemen",
|
||||
"cmd.component_replace_with_link": "Vervangen door koppeling",
|
||||
"cmd.component_revert_position": "Positie terugzetten",
|
||||
"cmd.component_show_history": "Componentgeschiedenis tonen",
|
||||
"cmd.component_unlock": "Inhoud ontgrendelen",
|
||||
"cmd.component_update_links": "Koppelingen bijwerken",
|
||||
"cmd.config_activate": "Configuratie activeren",
|
||||
@@ -885,6 +889,7 @@
|
||||
"cmd.config_rule": "Configuratieregel",
|
||||
"cmd.config_rules": "Configuratieregels",
|
||||
"cmd.config_table": "Configuratietabel tonen",
|
||||
"cmd.construct.plane.axis_point": "Vlak door as en punt",
|
||||
"cmd.construct_axis_edge": "As door rand",
|
||||
"cmd.construct_axis_normal_to_face": "As loodrecht op vlak",
|
||||
"cmd.construct_axis_through_cylinder": "As door cilinder/kegel/torus",
|
||||
@@ -933,6 +938,11 @@
|
||||
"cmd.explode_show": "Explosieweergave tonen",
|
||||
"cmd.expr_evaluate": "Expressie evalueren",
|
||||
"cmd.face_delete": "Vlak verwijderen",
|
||||
"cmd.file.export_3mf": "3MF exporteren",
|
||||
"cmd.file.export_obj": "OBJ exporteren",
|
||||
"cmd.file.export_step": "STEP exporteren",
|
||||
"cmd.file.export_stl": "STL exporteren",
|
||||
"cmd.file.export_urdf": "URDF exporteren",
|
||||
"cmd.file_export": "Exporteren",
|
||||
"cmd.file_insert_mesh": "Mesh invoegen",
|
||||
"cmd.file_insert_step": "STEP invoegen",
|
||||
@@ -950,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "Componentkleuren inschakelen",
|
||||
"cmd.inspect_colored_components_disable": "Componentkleuren uitschakelen",
|
||||
"cmd.inspect_compare": "Lichamen vergelijken",
|
||||
"cmd.inspect_compare_picture": "Afbeelding vergelijken",
|
||||
"cmd.inspect_continuity": "Continuïteitsanalyse",
|
||||
"cmd.inspect_curvature_comb": "Krommingskamanalyse",
|
||||
"cmd.inspect_curvature_map": "Krommingskaartanalyse",
|
||||
@@ -1040,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "Puntenwolk bewerken",
|
||||
"cmd.pointcloud_insert": "Puntenwolk invoegen",
|
||||
"cmd.pointcloud_list": "Puntenwolken",
|
||||
"cmd.render.compare.divider": "Scheidingslijn van de rendervergelijking verplaatsen",
|
||||
"cmd.render.result.discard": "Renderresultaat verwerpen",
|
||||
"cmd.render.result.select": "Renderresultaat selecteren",
|
||||
"cmd.render_aging_describe": "Gelaagde oppervlakveroudering",
|
||||
"cmd.render_backends": "Render-backends",
|
||||
"cmd.render_batch": "Alle renderopnamen renderen",
|
||||
@@ -1254,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "Tijdlijnmarkering verplaatsen",
|
||||
"cmd.timeline_stop": "Geschiedenis stoppen",
|
||||
"cmd.timeline_suppress": "Kenmerk onderdrukken",
|
||||
"cmd.ui.assistant": "Assistent tonen/verbergen",
|
||||
"cmd.view.render_browser": "Renderbrowser",
|
||||
"cmd.view.render_dock": "Renderdock verplaatsen",
|
||||
"cmd.view.render_focus": "Renderfocusweergave",
|
||||
@@ -1288,9 +1303,11 @@
|
||||
"cmd.view_tool": "Navigatiegereedschap",
|
||||
"cmd.view_top": "Bovenaanzicht",
|
||||
"cmd.view_visual_style": "Visuele stijl",
|
||||
"color.blue": "Blauw",
|
||||
"color.copy": "Klik om kleurwaarden te kopiëren",
|
||||
"color.edit": "Klik om kleur te bewerken",
|
||||
"color.gamma": "Kleurwaarden worden getoond in gammaruimte 0-255",
|
||||
"color.green": "Groen",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "Voer #rrggbb of #rrggbbaa in; met zes cijfers blijft de dekking behouden",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1298,6 +1315,7 @@
|
||||
"color.linear": "Kleurwaarden worden getoond in lineaire ruimte 0-1",
|
||||
"color.recent": "Recente kleuren",
|
||||
"color.recent_help": "Kleuren die je in een kleurveld kiest, verschijnen hier",
|
||||
"color.red": "Rood",
|
||||
"color.saturation": "Verzadiging (procent)",
|
||||
"color.saturation_value": "Verzadiging en waarde",
|
||||
"color.selected": "Geselecteerde kleur",
|
||||
@@ -1305,6 +1323,8 @@
|
||||
"color.step_up": "Stap omhoog",
|
||||
"color.value": "Waarde, de helderheid (procent)",
|
||||
"config.column_filter_empty": "Geen overeenkomende kolommen",
|
||||
"config.rule_parameter_columns": "Alleen parameters met een kolom in de tabel worden weergegeven. Voeg een kolom toe met Kolom toevoegen (+) om de parameter hier te gebruiken.",
|
||||
"config.rule_suppression_columns": "Alleen kenmerken met een kolom Onderdrukken in de tabel worden weergegeven. Voeg een kolom toe met Kolom toevoegen (+) om de onderdrukking hier aan te sturen.",
|
||||
"contact.components_count": "{count} componenten",
|
||||
"contact.enabled_label": "ingeschakeld",
|
||||
"coupling.gear_1": "Tandwiel 1",
|
||||
@@ -1353,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "Kies lichamen of kenmerken: een schets is geen van beide. Herhaal de curven ervan met het eigen Patroon of Spiegelen van de schets, of extrudeer de schets eerst.",
|
||||
"dialog.select": "↖ Selecteren",
|
||||
"dialog.selected_count": "↖ {count} geselecteerd",
|
||||
"dialog.selection_constraint": "Beperking {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · curve {id}",
|
||||
"dialog.selection_guidance": "Selecteer een invoer om deze te wijzigen. Klik op geselecteerde objecten om ze te verwijderen; Ctrl/Cmd-klik schakelt ze ook om. Controleer het voorbeeld en druk daarna op OK.",
|
||||
"dialog.selection_midpoint": "Middelpunt · {id}",
|
||||
"dialog.selection_profile": "{sketch} · profiel {number}",
|
||||
"dialog.selection_step": "{step} van {total}: {input} selecteren",
|
||||
"dialog.step": "Stap",
|
||||
"document.changed_on_disk": "{name} is op schijf gewijzigd",
|
||||
@@ -1436,6 +1460,7 @@
|
||||
"help.construct.axis.two_points": "Maakt een constructieas door twee punten, hoekpunten of schetspunten. Gebruik dit voor een as die tussen twee bekende posities loopt.",
|
||||
"help.construct.plane.along_path": "Maakt een constructievlak loodrecht op een rand of curve, op een positie erlangs. Gebruik dit om een profiel voor een sweep of een doorsnede van een pad te schetsen.",
|
||||
"help.construct.plane.at_angle": "Maakt een constructievlak dat onder een hoek is gedraaid ten opzichte van een vlak, om een as of rand. Gebruik dit voor schuine schetsen, zoals een hoekbeugel.",
|
||||
"help.construct.plane.axis_point": "Maakt een constructievlak dat een as en een punt buiten die as bevat. Kies een as van de oorsprong of een constructieas, een rechte rand of schetslijn, en daarna een hoekpunt, schetspunt of constructiepunt. Een punt op de as kan geen eenduidig vlak bepalen.",
|
||||
"help.construct.plane.midplane": "Maakt een constructievlak halverwege twee vlakken, of dat de hoek tussen twee vlakken halveert waar ze onder een hoek samenkomen. Gebruik dit als symmetrievlak om te spiegelen.",
|
||||
"help.construct.plane.offset": "Maakt een constructievlak evenwijdig aan een vlak, op een afstand of door een punt. Gebruik dit om op een vaste hoogte boven een vlak te schetsen.",
|
||||
"help.construct.plane.perpendicular": "Maakt een constructievlak dat een lijn of rand bevat en loodrecht op een vlak staat. Gebruik dit om rechtop op een model te schetsen, langs een rand.",
|
||||
@@ -1812,6 +1837,8 @@
|
||||
"joint_move.blocked_hint": "Controleer het verbindingstype (zoals Star), het vastzetten, de verbindingslimieten en door parameters aangestuurde waarden.",
|
||||
"joint_move.blocked_title": "Dit onderdeel kan niet worden verplaatst",
|
||||
"joint_move.coarse": "Rotatiestap dicht bij de as (°)",
|
||||
"joint_move.conflict": "Een verbinding van dit onderdeel conflicteert met de andere: {joint}.",
|
||||
"joint_move.conflict_hint": "Bewerk, onderdruk of verwijder de conflicterende verbinding zodat de andere verbindingen het onderdeel kunnen plaatsen.",
|
||||
"joint_move.distance": "Afstand vanaf het begin",
|
||||
"joint_move.done": "Gereed",
|
||||
"joint_move.fine": "Rotatiestap ver van de as (°)",
|
||||
@@ -1911,6 +1938,8 @@
|
||||
"motion.track_name": "Spoor {number}",
|
||||
"nav.alias.help": "Shift+Alt+linkerknop slepen draait, Shift+Alt+middenknop slepen pant, Shift+Alt+rechterknop slepen zoomt; middenknop slepen pant ook en rechterknop slepen draait",
|
||||
"nav.blender.help": "Middenknop slepen draait, Shift+middenknop pant, Ctrl+middenknop zoomt; rechterknop slepen heeft geen invloed op de camera",
|
||||
"nav.creo.help": "Middenknop slepen draait, Shift+middenknop pant, Ctrl+middenknop verticaal slepen zoomt (omlaag om in te zoomen); scrollen met het wiel zoomt",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "Muis en 3D-muis draaien door beide polen heen en behouden de rolhoek. Het snappen op assen met Shift is uitgeschakeld.",
|
||||
"nav.free_orbit.label": "Vrij draaien",
|
||||
"nav.free_orbit.snap_help": "Muis en 3D-muis draaien door beide polen heen en behouden de rolhoek. Houd Shift ingedrukt tijdens het slepen om de kijkrichting en de rolhoek te snappen.",
|
||||
@@ -2063,12 +2092,19 @@
|
||||
"parts.family.button_head_cap_screw": "Bolkopschroef met inbus",
|
||||
"parts.family.dowel_pin": "Parallelpen (paspen)",
|
||||
"parts.family.hex_bolt": "Zeskantbout / zeskantschroef",
|
||||
"parts.family.hex_cap_screw_inch": "Zeskantschroef (inch)",
|
||||
"parts.family.hex_nut": "Zeskantmoer",
|
||||
"parts.family.hex_nut_inch": "Zeskantmoer (inch)",
|
||||
"parts.family.set_screw": "Stelschroef met inbus, vlakke punt",
|
||||
"parts.family.socket_head_cap_screw": "Cilinderkopschroef met inbus",
|
||||
"parts.family.washer": "Vlakke onderlegring",
|
||||
"parts.family.washer_sae": "Vlakke onderlegring, SAE (smal)",
|
||||
"parts.family.washer_uss": "Vlakke onderlegring, USS (breed)",
|
||||
"parts.holes": "Gaten (optioneel)",
|
||||
"parts.placement_hint": "kies een of meer gaten om in elk een onderdeel te plaatsen, of OK plaatst het in de oorsprong",
|
||||
"parts.system": "Eenhedensysteem",
|
||||
"parts.system.inch": "Inch",
|
||||
"parts.system.metric": "Metrisch",
|
||||
"pattern.alignment_2": "Uitlijning 2",
|
||||
"pattern.alignment_hint": "Aantallen omvatten het origineel. Gecentreerd verdeelt kopieën gelijkmatig; Omkeren kiest de extra zijde bij even totalen.",
|
||||
"pattern.direction_1": "Richting 1",
|
||||
@@ -2130,6 +2166,8 @@
|
||||
"prefs.button_left": "Linkerknop slepen",
|
||||
"prefs.button_middle": "Middenknop slepen",
|
||||
"prefs.button_right": "Rechterknop slepen",
|
||||
"prefs.carousel_next": "Volgende",
|
||||
"prefs.carousel_previous": "Vorige",
|
||||
"prefs.choose_font": "Lettertypebestand kiezen…",
|
||||
"prefs.click": "Klik",
|
||||
"prefs.contact_shading": "Zachte contactschaduw",
|
||||
@@ -2142,6 +2180,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Gebogen oppervlakken",
|
||||
"prefs.curves": "Gebogen oppervlakken",
|
||||
"prefs.cut_preview_tint": "Verwijderd materiaal rood tonen",
|
||||
"prefs.cut_preview_tint_help": "Een voorbeeld vult het materiaal dat een kenmerk verwijdert, en de vorm die het vervangt, met een rood doorschijnend spookbeeld. Als dit uit staat, zijn die vullingen volledig transparant en worden alleen hun contouren getekend.",
|
||||
"prefs.dark_parts_shading": "Vorm van donkere onderdelen behouden",
|
||||
"prefs.dark_parts_shading_help": "Schaduw de weergave in lineair licht met een zachte hemelvulling, zodat donkere vlakken een verloop houden in plaats van zwart te worden",
|
||||
"prefs.dead_zone_note": "De gearceerde band is de dode zone: beweging daarbinnen wordt genegeerd.",
|
||||
@@ -2200,6 +2240,8 @@
|
||||
"prefs.interface_language": "Interfacetaal",
|
||||
"prefs.interface_note": "Hoe SolidCraft eruitziet en waar de onderdelen staan.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_curved_roll_arrows": "De gebogen rolpijlen omkeren",
|
||||
"prefs.invert_curved_roll_arrows_note": "De twee gebogen pijlen op de hoek van de kubus laten de weergave in het schermvlak rollen. Aan: de pijl met de klok mee rolt tegen de klok in en de pijl tegen de klok in rolt met de klok mee. Uit: elke pijl rolt de weergave in de richting waarin hij krult.",
|
||||
"prefs.invert_navigator_arrows": "De rotatiepijlen van de weergavekubus omkeren",
|
||||
"prefs.invert_navigator_arrows_note": "De pijlen rond de kubus laten de camera roteren in de richting waarin ze wijzen, zoals in Fusion en Inventor: de pijl links wijst naar rechts en zwaait de camera naar rechts. Uit: een pijl draait de kubus naar de kant waar hij staat, dus de linkerpijl zwaait de camera naar links. De gebogen rolpijlen worden hierdoor niet beïnvloed.",
|
||||
"prefs.keep_work_on_tab_switch": "Dialoogvensters en gereedschappen open houden bij het wisselen van ontwerp",
|
||||
@@ -2259,6 +2301,8 @@
|
||||
"prefs.placement": "Plaatsing",
|
||||
"prefs.prefer_free_sketch_points": "Verplaatsbare schetspunten voorrang geven als punten overlappen",
|
||||
"prefs.preview_choices": "Voorbeeld van je keuzes",
|
||||
"prefs.preview_tint_new_faces": "Nieuw oppervlak in een voorbeeld inkleuren",
|
||||
"prefs.preview_tint_new_faces_help": "Een voorbeeld van een kenmerk dat een lichaam wijzigt, kan het oppervlak dat het toevoegt in de voorbeeldkleur tekenen. Als dit uit staat, wordt het lichaam als zichzelf getekend.",
|
||||
"prefs.print_face_highlight": "Markering van het printvlak",
|
||||
"prefs.print_face_highlight_tip": "Hoe sterk het printvlak van een lichaam in de weergave wordt gekleurd en omlijnd. 0 % toont geen markering.",
|
||||
"prefs.profile_active": "Actief profiel",
|
||||
@@ -2556,6 +2600,10 @@
|
||||
"render.base_color": "Basiskleur · sRGB",
|
||||
"render.base_metallic": "Metaalfractie",
|
||||
"render.base_opaque_only": "Deze editor behoudt de semantiek van ondoorzichtige lichamen. Gebruik de expliciete glasbesturing voor optische transmissie.",
|
||||
"render.batch_all_shots": "Alle opnamen ({count})",
|
||||
"render.batch_empty": "Sla een opname op voordat alle opnamen worden gerenderd.",
|
||||
"render.batch_limit": "Alle opnamen renderen is beperkt tot 32 opnamen.",
|
||||
"render.batch_status": "{running} bezig · {waiting} wachten",
|
||||
"render.bloom_enable": "Bloom tonen",
|
||||
"render.bloom_help": "Creatieve gloed, alleen in de uitvoer, afkomstig van positieve lineaire highlights en verspreid met drie genormaliseerde boxpassen. De straal is in uitvoerpixels. Toegevoegd in lineair weergavelicht vóór sRGB; geen lens- of volumetrisch effect en geen verbetering van de convergentie. De ruwe EXR blijft ongewijzigd.",
|
||||
"render.bloom_opaque": "Bloom vereist ondoorzichtige uitvoer. Transparante PNG wordt geweigerd, niet stilzwijgend afgevlakt.",
|
||||
@@ -2666,6 +2714,13 @@
|
||||
"render.choose_look": "Kies eerst een uiterlijk in Uiterlijken",
|
||||
"render.chromatic_offset": "Chromatische randverschuiving",
|
||||
"render.clear_stage": "Opgeslagen decor verwijderen",
|
||||
"render.compare_divider": "Scheidingslijn vergelijking",
|
||||
"render.compare_live": "Live",
|
||||
"render.compare_live_preview": "Live voorbeeld",
|
||||
"render.compare_render": "Render {number}",
|
||||
"render.compare_side": "Render {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} s · {renderer} · vastgelegd bij revisie {revision}",
|
||||
"render.compare_swap": "Omwisselen",
|
||||
"render.copy_request": "JSON kopiëren",
|
||||
"render.cpu_aa_help": "Ruimtelijke samples: meer samples verbeteren randen en verlengen de rendertijd.",
|
||||
"render.cpu_area_add": "Rechthoekig CPU-licht toevoegen",
|
||||
@@ -3415,6 +3470,10 @@
|
||||
"render.result_retained": "De laatste voltooide afbeelding wordt getoond; een nieuwere render heeft die nog niet vervangen.",
|
||||
"render.result_sequence": "Controlepunt",
|
||||
"render.result_title": "Renderresultaat",
|
||||
"render.results_choose": "Bewaarde resultaten",
|
||||
"render.results_label": "Render {number} · {shot} · {seconds} s",
|
||||
"render.results_stale_label": "{result} [verouderd]",
|
||||
"render.results_unnamed": "Afbeelding",
|
||||
"render.return_design": "Terug naar Ontwerp",
|
||||
"render.review_stale": "Deze beoordeling is verouderd. Los het op of controleer opnieuw voordat je toepast.",
|
||||
"render.ribbon_aging": "Veroudering",
|
||||
@@ -3898,10 +3957,7 @@
|
||||
"the two origins are on the same component": "de twee oorsprongen liggen op hetzelfde component",
|
||||
"thread.automatic_size": "Automatische grootte",
|
||||
"thread.designation": "Aanduiding",
|
||||
"thread.direction": "Richting van schroefdraad",
|
||||
"thread.iso_metric": "ISO metrisch",
|
||||
"thread.left_hand": "Linksdraaiend",
|
||||
"thread.right_hand": "Rechtsdraaiend",
|
||||
"thread.select_size": "Grootte selecteren",
|
||||
"timeline.delete_after_marker_one": "1 kenmerk na geschiedenismarkering verwijderen",
|
||||
"timeline.delete_after_marker_other": "{count} kenmerken na geschiedenismarkering verwijderen",
|
||||
@@ -4075,6 +4131,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Voer dit uit in een terminal en druk daarna opnieuw op Verbinden:",
|
||||
"ui.agent_chat_auth_title": "Aanmelden bij de agent",
|
||||
"ui.agent_chat_cancel": "Annuleren",
|
||||
"ui.agent_chat_cancel_timeout": "De agent reageerde niet op Stoppen en is losgekoppeld. Verbind opnieuw om door te gaan.",
|
||||
"ui.agent_chat_cannot_start": "Geïnstalleerd, maar start niet: {error}",
|
||||
"ui.agent_chat_changes": "Wijzigingen: {n}, één stap ongedaan maken",
|
||||
"ui.agent_chat_changes_n": "{n} wijzigingen",
|
||||
@@ -4084,11 +4141,13 @@
|
||||
"ui.agent_chat_command": "Agentopdracht",
|
||||
"ui.agent_chat_connect": "Verbinden",
|
||||
"ui.agent_chat_copy": "Kopiëren",
|
||||
"ui.agent_chat_copy_message": "Bericht kopiëren",
|
||||
"ui.agent_chat_ctx_bodies": "Lichamen",
|
||||
"ui.agent_chat_ctx_elements": "Elementen",
|
||||
"ui.agent_chat_ctx_features": "Kenmerken",
|
||||
"ui.agent_chat_ctx_more": "+{n} meer",
|
||||
"ui.agent_chat_ctx_other": "Overig",
|
||||
"ui.agent_chat_custom_heading": "Eigen model",
|
||||
"ui.agent_chat_custom_hint": "Elk programma dat het Agent Client Protocol spreekt via stdin en stdout. Er wordt geen shell gebruikt.",
|
||||
"ui.agent_chat_denied": "Geweigerd",
|
||||
"ui.agent_chat_deny": "Weigeren",
|
||||
@@ -4132,8 +4191,16 @@
|
||||
"ui.agent_chat_mode_all": "Alles",
|
||||
"ui.agent_chat_mode_ask": "Vragen",
|
||||
"ui.agent_chat_mode_edits": "Bewerken",
|
||||
"ui.agent_chat_model_custom": "Eigen model gebruiken: {model}",
|
||||
"ui.agent_chat_model_no_tools": "{model} kan de gereedschappen van SolidCraft niet gebruiken en kan dus alleen chatten. Kies een model met ondersteuning voor gereedschappen.",
|
||||
"ui.agent_chat_model_rejected": "De agent accepteerde {model} niet: {error}",
|
||||
"ui.agent_chat_model_search": "Modellen zoeken…",
|
||||
"ui.agent_chat_model_tip": "Het model dat de agent gebruikt. Uw keuze wordt voor deze agent onthouden.",
|
||||
"ui.agent_chat_models_failed": "Modellen konden niet worden opgehaald: {error}",
|
||||
"ui.agent_chat_models_heading": "Beschikbare modellen",
|
||||
"ui.agent_chat_models_listing": "Modellen ophalen…",
|
||||
"ui.agent_chat_models_none": "Het eindpunt geeft geen modellen weer",
|
||||
"ui.agent_chat_models_not_a_list": "Het antwoord was geen lijst met modellen",
|
||||
"ui.agent_chat_new": "Nieuwe chat",
|
||||
"ui.agent_chat_new_confirm": "Een nieuwe chat starten? De huidige wordt gewist.",
|
||||
"ui.agent_chat_next_picture": "Volgende afbeelding",
|
||||
@@ -4407,14 +4474,21 @@
|
||||
"ui.construction.contact_hint": "Raaklijn: kies een cirkel/boog of open spline/kegelsnede en dan het raakpunt (vrije krommen: eindpunten)",
|
||||
"ui.construction.external_hint": "Raaklijn: kies een cirkel of boog, een extern punt en dan de gewenste oplossing",
|
||||
"ui.construction.free_hint": "Constructielijn: klik op twee punten; klik met rechts om opnieuw te beginnen",
|
||||
"ui.construction.normal_needs_length": "Een oneindige lijn is altijd een constructielijn: typ een lengte om een normale lijn te tekenen",
|
||||
"ui.construction.point_hint": "Constructielijn: klik op het punt waar de lijn doorheen moet",
|
||||
"ui.construction.reference_hint": "Constructielijn: kies een rechte lijn en dan een punt; klik met rechts om de referentie te wijzigen",
|
||||
"ui.construction.ribbon_label": "Hulplijnen",
|
||||
"ui.construction.side_hint": "Constructielijn: klik op de zijde waarheen de lijn loopt; klik met rechts om opnieuw te beginnen",
|
||||
"ui.context_menu": "Contextmenu",
|
||||
"ui.continuity_crease": "Knik",
|
||||
"ui.continuity_curvature": "Kromming",
|
||||
"ui.continuity_curvature_change": "Krommingsverandering",
|
||||
"ui.continuity_g0": "G0-knik",
|
||||
"ui.continuity_g1": "G1-tangentieel",
|
||||
"ui.continuity_g2": "G2-kromming",
|
||||
"ui.continuity_gap": "Opening",
|
||||
"ui.continuity_position": "Positie",
|
||||
"ui.continuity_tangent": "Tangentieel",
|
||||
"ui.copy": "Kopiëren",
|
||||
"ui.corner": "Hoek",
|
||||
"ui.corners": "Hoeken",
|
||||
@@ -4675,6 +4749,7 @@
|
||||
"ui.hideOrigin": "Oorsprong verbergen",
|
||||
"ui.hidePlanes": "Vlakken verbergen",
|
||||
"ui.hideProfile": "Profiel verbergen",
|
||||
"ui.hide_component_history": "Geschiedenis ervan verbergen",
|
||||
"ui.hide_origin_by_default": "Oorsprong standaard verbergen",
|
||||
"ui.hide_origin_note": "Nieuwe ontwerpen beginnen met de map Oorsprong verborgen. Een ontwerp behoudt zijn eigen instelling voor Oorsprong zodra je die toont of verbergt.",
|
||||
"ui.history_back": "Eén stap terug",
|
||||
@@ -4682,6 +4757,7 @@
|
||||
"ui.history_edge_hint": "dubbelklik op de rand",
|
||||
"ui.history_first": "Naar het begin",
|
||||
"ui.history_forward": "Eén stap vooruit",
|
||||
"ui.history_hide_hidden": "Verborgen componenten verbergen",
|
||||
"ui.history_hide_names": "Namen verbergen",
|
||||
"ui.history_last": "Naar het einde",
|
||||
"ui.history_marker_tip": "Geschiedenismarkering: sleep om het model terug te draaien; nieuwe kenmerken komen hier",
|
||||
@@ -4691,10 +4767,12 @@
|
||||
"ui.history_play_button_show": "Afspeelknop tonen",
|
||||
"ui.history_play_finish_action": "Voltooi de huidige actie voordat u de geschiedenis afspeelt",
|
||||
"ui.history_play_roll_back": "Verplaats de geschiedenismarkering naar achteren voordat u afspeelt",
|
||||
"ui.history_show_hidden": "Verborgen componenten tonen",
|
||||
"ui.history_show_names": "Namen tonen",
|
||||
"ui.history_sketch_listed_under": "Vermeld onder {feature}",
|
||||
"ui.history_title": "Geschiedenis",
|
||||
"ui.history_toggle_names": "Kenmerknamen tonen of verbergen",
|
||||
"ui.history_waits_for_editor": "Ongedaan maken, Opnieuw en Geschiedenis wachten tot de kenmerkeditor is geopend",
|
||||
"ui.hole": "Gat",
|
||||
"ui.hole_tap_type": "Type tapgat",
|
||||
"ui.hole_type": "Gattype",
|
||||
@@ -4975,6 +5053,7 @@
|
||||
"ui.showDimension": "Maat tonen",
|
||||
"ui.showOrigin": "Oorsprong tonen",
|
||||
"ui.showPlanes": "Vlakken tonen",
|
||||
"ui.show_component_history": "Geschiedenis ervan tonen",
|
||||
"ui.show_constraints": "Beperkingen",
|
||||
"ui.show_construction_geometries": "Constructiegeometrieën",
|
||||
"ui.show_dimensions": "Maatvoering",
|
||||
@@ -5157,7 +5236,7 @@
|
||||
"update.check_unavailable": "Controleren op updates is niet beschikbaar voor deze installatie.",
|
||||
"update.checking": "Controleren…",
|
||||
"update.copy": "Kopiëren",
|
||||
"update.dmg_opened": "Het schijfkopie is geopend en geverifieerd: sleep SolidCraft naar Programma's, vervang de oude kopie en start SolidCraft daarna opnieuw.",
|
||||
"update.dmg_opened": "Het schijfkopie is geopend en geverifieerd: stop SolidCraft, sleep de nieuwe SolidCraft naar Programma's, vervang de oude kopie en open SolidCraft daarna opnieuw.",
|
||||
"update.download": "Downloaden",
|
||||
"update.download_open": "Downloaden en openen",
|
||||
"update.downloading": "Downloaden en verifiëren… {done} van {total} MB",
|
||||
@@ -5201,6 +5280,7 @@
|
||||
"web.open_link": "Gekoppeld ontwerp openen?",
|
||||
"web.open_link_confirm": "Er zijn niet-opgeslagen wijzigingen. Het gekoppelde ontwerp in een nieuw tabblad openen?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ kiezen · Enter / Tab aanvullen · Esc sluiten",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ kiezen · Enter past deze waarde toe",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ kiezen · Enter past toe wat je typte · Esc sluiten",
|
||||
"⌘Enter continues on GitHub.": "⌘Enter gaat verder op GitHub."
|
||||
}
|
||||
|
||||
@@ -2249,10 +2249,7 @@
|
||||
"the two origins are on the same component": "oba początki leżą na tym samym komponencie",
|
||||
"thread.automatic_size": "Rozmiar automatyczny",
|
||||
"thread.designation": "Oznaczenie",
|
||||
"thread.direction": "Kierunek gwintu",
|
||||
"thread.iso_metric": "Metryczny ISO",
|
||||
"thread.left_hand": "Lewy",
|
||||
"thread.right_hand": "Prawy",
|
||||
"thread.select_size": "Wybierz rozmiar",
|
||||
"timeline.delete_after_marker_one": "Usuń 1 funkcję za znacznikiem historii",
|
||||
"timeline.delete_after_marker_other": "Usuń funkcje za znacznikiem historii: {count}",
|
||||
@@ -2739,10 +2736,15 @@
|
||||
"ui.construction": "Konstrukcyjne",
|
||||
"ui.construction.ribbon_label": "Linie pom.",
|
||||
"ui.context_menu": "Menu kontekstowe",
|
||||
"ui.continuity_crease": "Załamanie",
|
||||
"ui.continuity_curvature": "Krzywizna",
|
||||
"ui.continuity_curvature_change": "Zmiana krzywizny",
|
||||
"ui.continuity_g0": "G0 załamanie",
|
||||
"ui.continuity_g1": "G1 styczność",
|
||||
"ui.continuity_g2": "G2 krzywizna",
|
||||
"ui.continuity_gap": "Szczelina",
|
||||
"ui.continuity_position": "Położenie",
|
||||
"ui.continuity_tangent": "Styczność",
|
||||
"ui.copy": "Kopiuj",
|
||||
"ui.corner": "Naroże",
|
||||
"ui.corners": "Naroża",
|
||||
@@ -3418,7 +3420,7 @@
|
||||
"update.check_unavailable": "Sprawdzanie aktualizacji nie jest dostępne dla tej instalacji.",
|
||||
"update.checking": "Sprawdzanie…",
|
||||
"update.copy": "Kopiuj",
|
||||
"update.dmg_opened": "Obraz dysku jest otwarty i zweryfikowany: przeciągnij SolidCraft do folderu Aplikacje, zastępując starą kopię, a następnie uruchom SolidCraft ponownie.",
|
||||
"update.dmg_opened": "Obraz dysku jest otwarty i zweryfikowany: zakończ SolidCraft, przeciągnij nowy SolidCraft do folderu Aplikacje, zastępując starą kopię, a następnie ponownie otwórz SolidCraft.",
|
||||
"update.download": "Pobierz",
|
||||
"update.download_open": "Pobierz i otwórz",
|
||||
"update.downloading": "Pobieranie i weryfikacja… {done} z {total} MB",
|
||||
|
||||
@@ -1219,6 +1219,8 @@
|
||||
"prefs.axis": "Eixo",
|
||||
"prefs.bottom": "Embaixo",
|
||||
"prefs.browser": "Navegador",
|
||||
"prefs.browser_components_open": "Componentes começam abertos",
|
||||
"prefs.browser_components_open_note": "Cada componente no navegador começa aberto, como antes, em vez de fechado até que você o abra.",
|
||||
"prefs.built_in": "Integrado",
|
||||
"prefs.button_left": "Arrastar com o esquerdo",
|
||||
"prefs.button_middle": "Arrastar com o meio",
|
||||
@@ -1883,10 +1885,15 @@
|
||||
"ui.construction": "Construção",
|
||||
"ui.construction.ribbon_label": "Linhas aux.",
|
||||
"ui.context_menu": "Menu de contexto",
|
||||
"ui.continuity_crease": "Vinco",
|
||||
"ui.continuity_curvature": "Curvatura",
|
||||
"ui.continuity_curvature_change": "Variação de curvatura",
|
||||
"ui.continuity_g0": "G0 vinco",
|
||||
"ui.continuity_g1": "G1 tangente",
|
||||
"ui.continuity_g2": "G2 curvatura",
|
||||
"ui.continuity_gap": "Folga",
|
||||
"ui.continuity_position": "Posição",
|
||||
"ui.continuity_tangent": "Tangente",
|
||||
"ui.copy": "Copiar",
|
||||
"ui.corner": "Canto",
|
||||
"ui.corners": "Cantos",
|
||||
@@ -2406,7 +2413,7 @@
|
||||
"update.check_unavailable": "A verificação de atualizações não está disponível para esta instalação.",
|
||||
"update.checking": "Verificando…",
|
||||
"update.copy": "Copiar",
|
||||
"update.dmg_opened": "A imagem de disco está aberta e verificada: arraste o SolidCraft para Aplicativos, substituindo a cópia antiga, e reinicie o SolidCraft.",
|
||||
"update.dmg_opened": "A imagem de disco está aberta e verificada: encerre o SolidCraft, arraste o novo SolidCraft para Aplicativos, substituindo a cópia antiga, e abra o SolidCraft novamente.",
|
||||
"update.download": "Baixar",
|
||||
"update.download_open": "Baixar e abrir",
|
||||
"update.downloading": "Baixando e verificando… {done} de {total} MB",
|
||||
|
||||
+85
-4
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "Dreptunghiular",
|
||||
"Rectangular Tube": "Tub dreptunghiular",
|
||||
"Reference face": "Față de referință",
|
||||
"Reference plane": "Plan de referință",
|
||||
"Relief Depth": "Adâncime relief",
|
||||
"Relief Width": "Lățime relief",
|
||||
"Remove Tools": "Elimină sculele",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "Secțiune",
|
||||
"Section (optional)": "Secțiune (opțional)",
|
||||
"Section position": "Poziția secțiunii",
|
||||
"Sections": "Secțiuni",
|
||||
"Sections (one sketch each)": "Secțiuni (câte o schiță)",
|
||||
"Select Other": "Selectează altul",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Selectează altul: țineți apăsat butonul stâng nemișcat pe model (sau clic dreapta › Selectează altul…) pentru a lista tot ce se află sub cursor; faceți clic din nou în același loc pentru a selecta următorul element. Dublu clic pe butonul din mijloc pentru a merge Acasă (Preferințe › Navigare).",
|
||||
@@ -859,6 +861,7 @@
|
||||
"cmd.component_extract": "Extrage în fișier",
|
||||
"cmd.component_finish_edit": "Finalizează editarea",
|
||||
"cmd.component_from_bodies": "Creează componente din corpuri",
|
||||
"cmd.component_hide_history": "Ascunde istoricul componentei",
|
||||
"cmd.component_insert_design": "Inserează proiect",
|
||||
"cmd.component_links": "Listează legăturile",
|
||||
"cmd.component_list": "Listează componentele",
|
||||
@@ -875,6 +878,7 @@
|
||||
"cmd.component_rename": "Redenumește componenta",
|
||||
"cmd.component_replace_with_link": "Înlocuiește cu legătură",
|
||||
"cmd.component_revert_position": "Revino la poziție",
|
||||
"cmd.component_show_history": "Afișează istoricul componentei",
|
||||
"cmd.component_unlock": "Deblochează conținutul",
|
||||
"cmd.component_update_links": "Actualizează legăturile",
|
||||
"cmd.config_activate": "Activează configurația",
|
||||
@@ -885,6 +889,7 @@
|
||||
"cmd.config_rule": "Regulă de configurație",
|
||||
"cmd.config_rules": "Reguli de configurație",
|
||||
"cmd.config_table": "Afișează tabelul de configurații",
|
||||
"cmd.construct.plane.axis_point": "Plan printr-o axă și un punct",
|
||||
"cmd.construct_axis_edge": "Axă prin muchie",
|
||||
"cmd.construct_axis_normal_to_face": "Axă perpendiculară pe față",
|
||||
"cmd.construct_axis_through_cylinder": "Axă prin cilindru/con/tor",
|
||||
@@ -933,6 +938,11 @@
|
||||
"cmd.explode_show": "Afișează vederea explodată",
|
||||
"cmd.expr_evaluate": "Evaluează expresia",
|
||||
"cmd.face_delete": "Șterge fața",
|
||||
"cmd.file.export_3mf": "Exportă 3MF",
|
||||
"cmd.file.export_obj": "Exportă OBJ",
|
||||
"cmd.file.export_step": "Exportă STEP",
|
||||
"cmd.file.export_stl": "Exportă STL",
|
||||
"cmd.file.export_urdf": "Exportă URDF",
|
||||
"cmd.file_export": "Exportă",
|
||||
"cmd.file_insert_mesh": "Inserează plasă",
|
||||
"cmd.file_insert_step": "Inserează STEP",
|
||||
@@ -950,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "Activează culorile componentelor",
|
||||
"cmd.inspect_colored_components_disable": "Dezactivează culorile componentelor",
|
||||
"cmd.inspect_compare": "Compară corpurile",
|
||||
"cmd.inspect_compare_picture": "Compară imaginea",
|
||||
"cmd.inspect_continuity": "Analiza continuității",
|
||||
"cmd.inspect_curvature_comb": "Analiza pieptenelui de curbură",
|
||||
"cmd.inspect_curvature_map": "Analiza hărții de curbură",
|
||||
@@ -1040,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "Editează norul de puncte",
|
||||
"cmd.pointcloud_insert": "Inserează nor de puncte",
|
||||
"cmd.pointcloud_list": "Nori de puncte",
|
||||
"cmd.render.compare.divider": "Mută separatorul comparației de randare",
|
||||
"cmd.render.result.discard": "Renunță la rezultatul de randare",
|
||||
"cmd.render.result.select": "Alege rezultatul de randare",
|
||||
"cmd.render_aging_describe": "Îmbătrânirea stratificată a suprafeței",
|
||||
"cmd.render_backends": "Motoare de randare",
|
||||
"cmd.render_batch": "Randează toate cadrele",
|
||||
@@ -1254,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "Mută marcajul cronologiei",
|
||||
"cmd.timeline_stop": "Oprește istoricul",
|
||||
"cmd.timeline_suppress": "Suprimă operația",
|
||||
"cmd.ui.assistant": "Afișează/Ascunde asistentul",
|
||||
"cmd.view.render_browser": "Navigator de randare",
|
||||
"cmd.view.render_dock": "Mută panoul de randare",
|
||||
"cmd.view.render_focus": "Vizualizare de randare concentrată",
|
||||
@@ -1288,9 +1303,11 @@
|
||||
"cmd.view_tool": "Instrument de navigare",
|
||||
"cmd.view_top": "Vizualizare de sus",
|
||||
"cmd.view_visual_style": "Stil vizual",
|
||||
"color.blue": "Albastru",
|
||||
"color.copy": "Clic pentru a copia valorile culorii",
|
||||
"color.edit": "Clic pentru a edita culoarea",
|
||||
"color.gamma": "Se afișează valorile culorii în spațiul gamma 0-255",
|
||||
"color.green": "Verde",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "Introduceți #rrggbb sau #rrggbbaa; cu șase cifre se păstrează opacitatea",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1298,6 +1315,7 @@
|
||||
"color.linear": "Se afișează valorile culorii în spațiul liniar 0-1",
|
||||
"color.recent": "Culori recente",
|
||||
"color.recent_help": "Culorile pe care le alegeți în orice câmp de culoare apar aici",
|
||||
"color.red": "Roșu",
|
||||
"color.saturation": "Saturație (procente)",
|
||||
"color.saturation_value": "Saturație și valoare",
|
||||
"color.selected": "Culoare selectată",
|
||||
@@ -1305,6 +1323,8 @@
|
||||
"color.step_up": "Crește cu un pas",
|
||||
"color.value": "Valoare, luminozitatea (procente)",
|
||||
"config.column_filter_empty": "Nicio coloană potrivită",
|
||||
"config.rule_parameter_columns": "Sunt listați doar parametrii care au o coloană în tabel. Adăugați una cu Adaugă coloană (+) ca să o folosiți aici.",
|
||||
"config.rule_suppression_columns": "Sunt listate doar operațiile care au o coloană Suprimă în tabel. Adăugați una cu Adaugă coloană (+) ca să le controlați de aici.",
|
||||
"contact.components_count": "{count} componente",
|
||||
"contact.enabled_label": "activat",
|
||||
"coupling.gear_1": "Roată dințată 1",
|
||||
@@ -1353,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "Alegeți corpuri sau operații: o schiță nu este niciuna dintre ele. Repetați curbele ei cu Matrice sau Oglindire din schiță, ori extrudați-o mai întâi.",
|
||||
"dialog.select": "↖ Selectează",
|
||||
"dialog.selected_count": "↖ {count} selectate",
|
||||
"dialog.selection_constraint": "Constrângerea {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · curba {id}",
|
||||
"dialog.selection_guidance": "Selectați o intrare pentru a o modifica. Faceți clic pe obiectele selectate pentru a le elimina; Ctrl/Cmd-clic le comută de asemenea. Verificați previzualizarea, apoi apăsați OK.",
|
||||
"dialog.selection_midpoint": "Punct de mijloc · {id}",
|
||||
"dialog.selection_profile": "{sketch} · profilul {number}",
|
||||
"dialog.selection_step": "{step} din {total}: Selectați {input}",
|
||||
"dialog.step": "Pas",
|
||||
"document.changed_on_disk": "{name} a fost modificat pe disc",
|
||||
@@ -1436,6 +1460,7 @@
|
||||
"help.construct.axis.two_points": "Creează o axă de construcție prin două puncte, vârfuri sau puncte de schiță. Folosiți-o pentru o axă care trece între două poziții cunoscute.",
|
||||
"help.construct.plane.along_path": "Creează un plan de construcție perpendicular pe o muchie sau o curbă, într-o poziție de-a lungul ei. Folosiți-l pentru a schița un profil de baleiere sau o secțiune prin traseu.",
|
||||
"help.construct.plane.at_angle": "Creează un plan de construcție rotit cu un unghi față de un plan sau o față, în jurul unei axe sau muchii. Folosiți-l pentru schițe înclinate, de exemplu un suport în unghi.",
|
||||
"help.construct.plane.axis_point": "Creează un plan de construcție care conține o axă și un punct din afara axei. Alegeți o origine sau o axă de construcție, o muchie dreaptă sau o linie de schiță, apoi un vârf, un punct de schiță sau un punct de construcție. Un punct situat pe axă nu poate defini un plan unic.",
|
||||
"help.construct.plane.midplane": "Creează un plan de construcție la jumătatea distanței dintre două plane sau fețe plane ori care le bisectează acolo unde se întâlnesc sub un unghi. Folosiți-l ca plan de simetrie pentru oglindire.",
|
||||
"help.construct.plane.offset": "Creează un plan de construcție paralel cu un plan sau o față plană, la o distanță sau printr-un punct. Folosiți-l pentru a schița la o înălțime stabilită deasupra unei fețe.",
|
||||
"help.construct.plane.perpendicular": "Creează un plan de construcție care conține o linie sau o muchie și este perpendicular pe un plan sau o față. Folosiți-l pentru a schița vertical pe un model, de-a lungul unei muchii.",
|
||||
@@ -1812,6 +1837,8 @@
|
||||
"joint_move.blocked_hint": "Verificați tipul îmbinării (de exemplu Rigidă), fixarea, limitele îmbinării și valorile conduse de parametri.",
|
||||
"joint_move.blocked_title": "Această piesă nu poate fi mutată",
|
||||
"joint_move.coarse": "Pas de rotire lângă axă (°)",
|
||||
"joint_move.conflict": "O îmbinare a acestei piese este în conflict cu celelalte: {joint}.",
|
||||
"joint_move.conflict_hint": "Editați, suprimați sau ștergeți îmbinarea în conflict, ca celelalte îmbinări să poată poziționa piesa.",
|
||||
"joint_move.distance": "Distanță față de start",
|
||||
"joint_move.done": "Gata",
|
||||
"joint_move.fine": "Pas de rotire departe de axă (°)",
|
||||
@@ -1911,6 +1938,8 @@
|
||||
"motion.track_name": "Pista {number}",
|
||||
"nav.alias.help": "Shift+Alt+tragere cu stânga orbitează, Shift+Alt+tragere cu mijlocul panoramează, Shift+Alt+tragere cu dreapta face zoom; tragerea cu mijlocul panoramează și ea, iar tragerea cu dreapta orbitează",
|
||||
"nav.blender.help": "Tragerea cu mijlocul orbitează, Shift+mijloc panoramează, Ctrl+mijloc face zoom; tragerea cu dreapta nu mișcă camera",
|
||||
"nav.creo.help": "Tragerea cu mijlocul orbitează, Shift+mijloc panoramează, tragerea verticală cu Ctrl+mijloc face zoom (în jos pentru apropiere); rotița mouse-ului face zoom",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "Mouse-ul și mouse-ul 3D rotesc vizualizarea prin ambii poli și păstrează ruliul. Agățarea pe axe cu Shift este dezactivată.",
|
||||
"nav.free_orbit.label": "Orbitare liberă",
|
||||
"nav.free_orbit.snap_help": "Mouse-ul și mouse-ul 3D rotesc vizualizarea prin ambii poli și păstrează ruliul. Țineți apăsat Shift în timpul tragerii de orbitare pentru a agăța direcția de privire și ruliul.",
|
||||
@@ -2063,12 +2092,19 @@
|
||||
"parts.family.button_head_cap_screw": "Șurub cu cap bombat și locaș hexagonal",
|
||||
"parts.family.dowel_pin": "Știft cilindric (de centrare)",
|
||||
"parts.family.hex_bolt": "Șurub / bolț cu cap hexagonal",
|
||||
"parts.family.hex_cap_screw_inch": "Șurub cu cap hexagonal (inci)",
|
||||
"parts.family.hex_nut": "Piuliță hexagonală",
|
||||
"parts.family.hex_nut_inch": "Piuliță hexagonală (inci)",
|
||||
"parts.family.set_screw": "Șurub de fixare cu locaș hexagonal, vârf plat",
|
||||
"parts.family.socket_head_cap_screw": "Șurub cu cap cilindric și locaș hexagonal",
|
||||
"parts.family.washer": "Șaibă plată",
|
||||
"parts.family.washer_sae": "Șaibă plată, SAE (îngustă)",
|
||||
"parts.family.washer_uss": "Șaibă plată, USS (lată)",
|
||||
"parts.holes": "Găuri (opțional)",
|
||||
"parts.placement_hint": "alegeți una sau mai multe găuri pentru a așeza câte o piesă în fiecare, sau OK plasează piesa în origine",
|
||||
"parts.system": "Sistem de unități",
|
||||
"parts.system.inch": "Inci",
|
||||
"parts.system.metric": "Metric",
|
||||
"pattern.alignment_2": "Aliniere 2",
|
||||
"pattern.alignment_hint": "Numărul include originalul. Centrat echilibrează copiile; Inversare alege partea suplimentară pentru totaluri pare.",
|
||||
"pattern.direction_1": "Direcția 1",
|
||||
@@ -2130,6 +2166,8 @@
|
||||
"prefs.button_left": "Tragere cu butonul stâng",
|
||||
"prefs.button_middle": "Tragere cu butonul din mijloc",
|
||||
"prefs.button_right": "Tragere cu butonul drept",
|
||||
"prefs.carousel_next": "Următorul",
|
||||
"prefs.carousel_previous": "Anteriorul",
|
||||
"prefs.choose_font": "Alegeți fișierul de font…",
|
||||
"prefs.click": "Clic",
|
||||
"prefs.contact_shading": "Umbrire de contact moale",
|
||||
@@ -2142,6 +2180,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Suprafețe curbe",
|
||||
"prefs.curves": "Suprafețe curbe",
|
||||
"prefs.cut_preview_tint": "Afișează materialul îndepărtat în roșu",
|
||||
"prefs.cut_preview_tint_help": "O previzualizare umple materialul pe care îl îndepărtează o operație, și forma pe care o înlocuiește, cu o fantomă roșie transparentă. Dezactivat, aceste umpleri sunt complet transparente și se desenează doar contururile lor.",
|
||||
"prefs.dark_parts_shading": "Păstrează forma pieselor întunecate",
|
||||
"prefs.dark_parts_shading_help": "Umbrește vizualizarea în lumină liniară, cu o lumină de umplere moale de la cer, astfel încât fețele întunecate să păstreze un gradient în loc să devină negre",
|
||||
"prefs.dead_zone_note": "Banda hașurată este zona moartă: mișcarea din interiorul ei este ignorată.",
|
||||
@@ -2200,6 +2240,8 @@
|
||||
"prefs.interface_language": "Limba interfeței",
|
||||
"prefs.interface_note": "Cum arată SolidCraft și unde sunt așezate elementele sale.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_curved_roll_arrows": "Inversează săgețile curbe de ruliu",
|
||||
"prefs.invert_curved_roll_arrows_note": "Cele două săgeți curbe din colțul cubului rotesc vizualizarea în planul ecranului. Activat: săgeata în sens orar rotește în sens antiorar, iar săgeata în sens antiorar rotește în sens orar. Dezactivat: fiecare săgeată rotește vizualizarea în direcția în care se curbează.",
|
||||
"prefs.invert_navigator_arrows": "Inversează săgețile de rotație ale cubului de vizualizare",
|
||||
"prefs.invert_navigator_arrows_note": "Săgețile din jurul cubului rotesc camera în jurul modelului în direcția în care indică, ca în Fusion și Inventor: săgeata din stânga indică spre dreapta și rotește camera spre dreapta. Dezactivat: o săgeată rotește cubul spre partea pe care se află, deci săgeata din stânga rotește camera spre stânga. Săgețile curbe de ruliu nu sunt afectate.",
|
||||
"prefs.keep_work_on_tab_switch": "Păstrează deschise dialogurile și instrumentele la schimbarea proiectului",
|
||||
@@ -2259,6 +2301,8 @@
|
||||
"prefs.placement": "Plasare",
|
||||
"prefs.prefer_free_sketch_points": "Preferă punctele de schiță mobile când punctele se suprapun",
|
||||
"prefs.preview_choices": "Previzualizarea opțiunilor alese",
|
||||
"prefs.preview_tint_new_faces": "Colorează suprafața nouă într-o previzualizare",
|
||||
"prefs.preview_tint_new_faces_help": "Previzualizarea unei operații care modifică un corp poate desena suprafața adăugată în culoarea previzualizării. Dezactivat, corpul este desenat ca atare.",
|
||||
"prefs.print_face_highlight": "Evidențierea feței de imprimare",
|
||||
"prefs.print_face_highlight_tip": "Cât de puternic este colorată și conturată fața de imprimare a unui corp în vizualizare. 0 % nu afișează evidențiere.",
|
||||
"prefs.profile_active": "Profil activ",
|
||||
@@ -2556,6 +2600,10 @@
|
||||
"render.base_color": "Culoare de bază · sRGB",
|
||||
"render.base_metallic": "Fracție metalică",
|
||||
"render.base_opaque_only": "Acest editor păstrează semantica corpurilor opace. Pentru transmisia optică folosiți controalele explicite pentru sticlă.",
|
||||
"render.batch_all_shots": "Toate cadrele ({count})",
|
||||
"render.batch_empty": "Salvați un cadru înainte de a randa toate cadrele.",
|
||||
"render.batch_limit": "Randarea tuturor cadrelor este limitată la 32 de cadre.",
|
||||
"render.batch_status": "{running} în desfășurare · {waiting} în așteptare",
|
||||
"render.bloom_enable": "Afișează bloom",
|
||||
"render.bloom_help": "Strălucire creativă, doar la ieșire, provenită din zonele luminoase liniare pozitive și răspândită prin trei treceri box normalizate. Raza este în pixeli de ieșire. Se adaugă în lumina de afișare liniară, înainte de sRGB; nu este un efect de lentilă, un efect volumetric sau o îmbunătățire a convergenței. EXR-ul brut rămâne neschimbat.",
|
||||
"render.bloom_opaque": "Bloom necesită ieșire opacă. PNG-ul transparent este refuzat, nu aplatizat în tăcere.",
|
||||
@@ -2666,6 +2714,13 @@
|
||||
"render.choose_look": "Alegeți mai întâi un stil în Aspecte",
|
||||
"render.chromatic_offset": "Decalaj cromatic al marginilor",
|
||||
"render.clear_stage": "Elimină decorul salvat",
|
||||
"render.compare_divider": "Separator de comparație",
|
||||
"render.compare_live": "Live",
|
||||
"render.compare_live_preview": "Previzualizare în timp real",
|
||||
"render.compare_render": "Randarea {number}",
|
||||
"render.compare_side": "Randarea {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} s · {renderer} · capturat la revizia {revision}",
|
||||
"render.compare_swap": "Schimbă",
|
||||
"render.copy_request": "Copiază JSON",
|
||||
"render.cpu_aa_help": "Eșantioane spațiale: mai multe eșantioane îmbunătățesc marginile și măresc timpul de randare.",
|
||||
"render.cpu_area_add": "Adaugă lumină dreptunghiulară CPU",
|
||||
@@ -3415,6 +3470,10 @@
|
||||
"render.result_retained": "Se afișează ultima imagine finalizată; o randare mai nouă nu a înlocuit-o.",
|
||||
"render.result_sequence": "Punct de control",
|
||||
"render.result_title": "Rezultatul randării",
|
||||
"render.results_choose": "Rezultate păstrate",
|
||||
"render.results_label": "Randarea {number} · {shot} · {seconds} s",
|
||||
"render.results_stale_label": "{result} [învechit]",
|
||||
"render.results_unnamed": "Imagine",
|
||||
"render.return_design": "Înapoi la proiectare",
|
||||
"render.review_stale": "Această revizuire este depășită. Rezolvați sau verificați din nou înainte de aplicare.",
|
||||
"render.ribbon_aging": "Îmbătrânire",
|
||||
@@ -3898,10 +3957,7 @@
|
||||
"the two origins are on the same component": "cele două origini sunt pe aceeași componentă",
|
||||
"thread.automatic_size": "Dimensiune automată",
|
||||
"thread.designation": "Denumire",
|
||||
"thread.direction": "Sensul filetului",
|
||||
"thread.iso_metric": "ISO metric",
|
||||
"thread.left_hand": "Pe stânga",
|
||||
"thread.right_hand": "Pe dreapta",
|
||||
"thread.select_size": "Selectați dimensiunea",
|
||||
"timeline.delete_after_marker_one": "Șterge 1 operație după marcajul de istoric",
|
||||
"timeline.delete_after_marker_other": "Șterge {count} operații după marcajul de istoric",
|
||||
@@ -4075,6 +4131,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Rulați aceasta într-un terminal, apoi apăsați din nou Conectare:",
|
||||
"ui.agent_chat_auth_title": "Autentificare la agent",
|
||||
"ui.agent_chat_cancel": "Anulează",
|
||||
"ui.agent_chat_cancel_timeout": "Agentul nu a răspuns la Oprește și a fost deconectat. Reconectați-vă pentru a continua.",
|
||||
"ui.agent_chat_cannot_start": "Instalat, dar nu pornește: {error}",
|
||||
"ui.agent_chat_changes": "Modificări: {n}, un singur pas de anulare",
|
||||
"ui.agent_chat_changes_n": "{n} modificări",
|
||||
@@ -4084,11 +4141,13 @@
|
||||
"ui.agent_chat_command": "Comanda agentului",
|
||||
"ui.agent_chat_connect": "Conectare",
|
||||
"ui.agent_chat_copy": "Copiază",
|
||||
"ui.agent_chat_copy_message": "Copiază mesajul",
|
||||
"ui.agent_chat_ctx_bodies": "Corpuri",
|
||||
"ui.agent_chat_ctx_elements": "Elemente",
|
||||
"ui.agent_chat_ctx_features": "Operații",
|
||||
"ui.agent_chat_ctx_more": "+{n} în plus",
|
||||
"ui.agent_chat_ctx_other": "Altele",
|
||||
"ui.agent_chat_custom_heading": "Model personalizat",
|
||||
"ui.agent_chat_custom_hint": "Orice program care vorbește Agent Client Protocol pe stdin și stdout. Nu este rulat de niciun shell.",
|
||||
"ui.agent_chat_denied": "Refuzat",
|
||||
"ui.agent_chat_deny": "Refuză",
|
||||
@@ -4132,8 +4191,16 @@
|
||||
"ui.agent_chat_mode_all": "Tot",
|
||||
"ui.agent_chat_mode_ask": "Întreabă",
|
||||
"ui.agent_chat_mode_edits": "Modificări",
|
||||
"ui.agent_chat_model_custom": "Folosește modelul personalizat: {model}",
|
||||
"ui.agent_chat_model_no_tools": "{model} nu poate folosi instrumentele SolidCraft, deci poate doar să converseze. Alegeți un model cu suport pentru instrumente.",
|
||||
"ui.agent_chat_model_rejected": "Agentul nu a acceptat {model}: {error}",
|
||||
"ui.agent_chat_model_search": "Caută modele…",
|
||||
"ui.agent_chat_model_tip": "Modelul folosit de agent. Alegerea dumneavoastră este reținută pentru acest agent.",
|
||||
"ui.agent_chat_models_failed": "Nu s-au putut lista modelele: {error}",
|
||||
"ui.agent_chat_models_heading": "Modele disponibile",
|
||||
"ui.agent_chat_models_listing": "Se listează modelele…",
|
||||
"ui.agent_chat_models_none": "Endpoint-ul nu listează niciun model",
|
||||
"ui.agent_chat_models_not_a_list": "Răspunsul nu a fost o listă de modele",
|
||||
"ui.agent_chat_new": "Chat nou",
|
||||
"ui.agent_chat_new_confirm": "Începeți un chat nou? Cel curent este șters.",
|
||||
"ui.agent_chat_next_picture": "Imaginea următoare",
|
||||
@@ -4407,14 +4474,21 @@
|
||||
"ui.construction.contact_hint": "Tangentă: alegeți un cerc/arc sau un spline/o curbă conică deschisă, apoi punctul de tangență (curbe de formă liberă: capetele)",
|
||||
"ui.construction.external_hint": "Tangentă: alegeți un cerc sau un arc, un punct exterior, apoi soluția dorită",
|
||||
"ui.construction.free_hint": "Dreaptă de construcție: clic pe două puncte; clic dreapta pentru a relua",
|
||||
"ui.construction.normal_needs_length": "O dreaptă infinită este întotdeauna de construcție: introduceți o lungime pentru a desena o linie normală",
|
||||
"ui.construction.point_hint": "Dreaptă de construcție: clic pe punctul prin care trece",
|
||||
"ui.construction.reference_hint": "Dreaptă de construcție: alegeți o linie dreaptă, apoi un punct; clic dreapta pentru a schimba referința",
|
||||
"ui.construction.ribbon_label": "Linii aux.",
|
||||
"ui.construction.side_hint": "Dreaptă de construcție: faceți clic pe partea spre care merge linia; clic dreapta pentru a relua",
|
||||
"ui.context_menu": "Meniu contextual",
|
||||
"ui.continuity_crease": "Muchie",
|
||||
"ui.continuity_curvature": "Curbură",
|
||||
"ui.continuity_curvature_change": "Variația curburii",
|
||||
"ui.continuity_g0": "Muchie G0",
|
||||
"ui.continuity_g1": "Tangent G1",
|
||||
"ui.continuity_g2": "Curbură G2",
|
||||
"ui.continuity_gap": "Spațiu",
|
||||
"ui.continuity_position": "Poziție",
|
||||
"ui.continuity_tangent": "Tangent",
|
||||
"ui.copy": "Copiază",
|
||||
"ui.corner": "Colț",
|
||||
"ui.corners": "Colțuri",
|
||||
@@ -4675,6 +4749,7 @@
|
||||
"ui.hideOrigin": "Ascunde originea",
|
||||
"ui.hidePlanes": "Ascunde planele",
|
||||
"ui.hideProfile": "Ascunde profilul",
|
||||
"ui.hide_component_history": "Ascunde istoricul ei",
|
||||
"ui.hide_origin_by_default": "Ascunde originea implicit",
|
||||
"ui.hide_origin_note": "Proiectele noi încep cu dosarul Origine ascuns. Un proiect își păstrează propria setare Origine după ce îl afișați sau îl ascundeți.",
|
||||
"ui.history_back": "Un pas înapoi",
|
||||
@@ -4682,6 +4757,7 @@
|
||||
"ui.history_edge_hint": "faceți dublu clic pe muchie",
|
||||
"ui.history_first": "Mergi la început",
|
||||
"ui.history_forward": "Un pas înainte",
|
||||
"ui.history_hide_hidden": "Ascunde componentele ascunse",
|
||||
"ui.history_hide_names": "Ascunde numele",
|
||||
"ui.history_last": "Mergi la sfârșit",
|
||||
"ui.history_marker_tip": "Marcaj de istoric: trageți-l pentru a derula modelul înapoi; operațiile noi se adaugă aici",
|
||||
@@ -4691,10 +4767,12 @@
|
||||
"ui.history_play_button_show": "Afișează butonul Redare",
|
||||
"ui.history_play_finish_action": "Finalizați acțiunea curentă înainte de a reda istoricul",
|
||||
"ui.history_play_roll_back": "Mutați marcajul istoricului înapoi înainte de redare",
|
||||
"ui.history_show_hidden": "Afișează componentele ascunse",
|
||||
"ui.history_show_names": "Afișează numele",
|
||||
"ui.history_sketch_listed_under": "Listată sub {feature}",
|
||||
"ui.history_title": "Istoric",
|
||||
"ui.history_toggle_names": "Afișează sau ascunde numele operațiilor",
|
||||
"ui.history_waits_for_editor": "Anulează, Refă și Istoric așteaptă până se deschide editorul operației",
|
||||
"ui.hole": "Gaură",
|
||||
"ui.hole_tap_type": "Tip de filetare gaură",
|
||||
"ui.hole_type": "Tip de gaură",
|
||||
@@ -4710,6 +4788,7 @@
|
||||
"ui.insert_design.saved_file": "{name} are modificări nesalvate, așa că a fost inserat fișierul său salvat. Salvați-l, apoi folosiți Actualizează legăturile pentru a aduce modificările.",
|
||||
"ui.insert_design.unsaved": "Modificări nesalvate",
|
||||
"ui.inspect": "Inspectează",
|
||||
"ui.instances": "Instanțe",
|
||||
"ui.intersect": "Intersecție",
|
||||
"ui.isolate": "Izolează",
|
||||
"ui.join": "Unire",
|
||||
@@ -4974,6 +5053,7 @@
|
||||
"ui.showDimension": "Afișează cota",
|
||||
"ui.showOrigin": "Afișează originea",
|
||||
"ui.showPlanes": "Afișează planele",
|
||||
"ui.show_component_history": "Afișează istoricul ei",
|
||||
"ui.show_constraints": "Constrângeri",
|
||||
"ui.show_construction_geometries": "Geometrii de construcție",
|
||||
"ui.show_dimensions": "Cote",
|
||||
@@ -5156,7 +5236,7 @@
|
||||
"update.check_unavailable": "Căutarea actualizărilor nu este disponibilă pentru această instalare.",
|
||||
"update.checking": "Se verifică…",
|
||||
"update.copy": "Copiază",
|
||||
"update.dmg_opened": "Imaginea de disc este deschisă și verificată: trageți SolidCraft în Applications, înlocuind copia veche, apoi reporniți SolidCraft.",
|
||||
"update.dmg_opened": "Imaginea de disc este deschisă și verificată: ieșiți din SolidCraft, trageți noul SolidCraft în Applications, înlocuind copia veche, apoi deschideți din nou SolidCraft.",
|
||||
"update.download": "Descarcă",
|
||||
"update.download_open": "Descarcă și deschide",
|
||||
"update.downloading": "Se descarcă și se verifică… {done} din {total} MB",
|
||||
@@ -5200,6 +5280,7 @@
|
||||
"web.open_link": "Deschideți proiectul legat?",
|
||||
"web.open_link_confirm": "Există modificări nesalvate. Deschideți proiectul legat într-o filă nouă?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ alege · Enter / Tab completează · Esc închide",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ alegeți · Enter aplică această valoare",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ alegeți · Enter aplică ce ați tastat · Esc închide",
|
||||
"⌘Enter continues on GitHub.": "⌘Enter continuă pe GitHub."
|
||||
}
|
||||
|
||||
+108
-5
@@ -115,7 +115,7 @@
|
||||
"Circular": "Круговой",
|
||||
"Circumference": "Окружность",
|
||||
"Clockwise": "По часовой стрелке",
|
||||
"Close draft": "Закрыть анализ уклонов",
|
||||
"Close draft": "Закрыть черновик",
|
||||
"Collapse": "Свернуть",
|
||||
"Collisions": "Столкновения",
|
||||
"Colour": "Цвет",
|
||||
@@ -475,6 +475,7 @@
|
||||
"Rectangular": "Прямоугольная",
|
||||
"Rectangular Tube": "Прямоугольная труба",
|
||||
"Reference face": "Базовая грань",
|
||||
"Reference plane": "Базовая плоскость",
|
||||
"Relief Depth": "Глубина разгрузки",
|
||||
"Relief Width": "Ширина разгрузки",
|
||||
"Remove Tools": "Убрать инструменты",
|
||||
@@ -554,6 +555,7 @@
|
||||
"Section": "Сечение",
|
||||
"Section (optional)": "Сечение (необязательно)",
|
||||
"Section position": "Положение сечения",
|
||||
"Sections": "Сечения",
|
||||
"Sections (one sketch each)": "Сечения (по одному эскизу)",
|
||||
"Select Other": "Выбрать другое",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Выбрать другое: удерживайте левую кнопку на модели (или правый щелчок › Выбрать другое…), чтобы увидеть всё под курсором; повторный щелчок в том же месте выбирает следующий объект. Двойной щелчок средней кнопкой — «Главная» (Настройки › Навигация).",
|
||||
@@ -775,6 +777,8 @@
|
||||
"at the face": "у грани",
|
||||
"bom.group_children": "Вложенные детали в строке своей детали",
|
||||
"browser.body_name": "Тело{number}",
|
||||
"browser.calibrate_click_a": "Щёлкните по изображению, чтобы задать точку A, или введите её координаты.",
|
||||
"browser.calibrate_click_b": "Щёлкните по изображению, чтобы задать точку B, или введите её координаты.",
|
||||
"browser.calibrate_title": "Калибровка: {name}",
|
||||
"browser.capture_many": "Компоненты ({count}) перемещены, но их положения не зафиксированы.",
|
||||
"browser.capture_note": "«Зафиксировать положение» сохраняет новое положение; «Вернуть положение» восстанавливает прежнее.",
|
||||
@@ -853,6 +857,7 @@
|
||||
"cmd.component_extract": "Извлечь в файл",
|
||||
"cmd.component_finish_edit": "Завершить изменение",
|
||||
"cmd.component_from_bodies": "Создать компоненты из тел",
|
||||
"cmd.component_hide_history": "Скрыть историю компонента",
|
||||
"cmd.component_insert_design": "Вставить дизайн",
|
||||
"cmd.component_links": "Список связей",
|
||||
"cmd.component_list": "Список компонентов",
|
||||
@@ -869,6 +874,7 @@
|
||||
"cmd.component_rename": "Переименовать компонент",
|
||||
"cmd.component_replace_with_link": "Заменить ссылкой",
|
||||
"cmd.component_revert_position": "Вернуть положение",
|
||||
"cmd.component_show_history": "Показать историю компонента",
|
||||
"cmd.component_unlock": "Разблокировать содержимое",
|
||||
"cmd.component_update_links": "Обновить связи",
|
||||
"cmd.config_activate": "Активировать конфигурацию",
|
||||
@@ -879,6 +885,7 @@
|
||||
"cmd.config_rule": "Правило конфигурации",
|
||||
"cmd.config_rules": "Правила конфигурации",
|
||||
"cmd.config_table": "Показать таблицу конфигурации",
|
||||
"cmd.construct.plane.axis_point": "Плоскость через ось и точку",
|
||||
"cmd.construct_axis_edge": "Ось через ребро",
|
||||
"cmd.construct_axis_normal_to_face": "Ось перпендикулярно грани",
|
||||
"cmd.construct_axis_through_cylinder": "Ось через цилиндр/конус/тор",
|
||||
@@ -927,6 +934,11 @@
|
||||
"cmd.explode_show": "Показать разобранный вид",
|
||||
"cmd.expr_evaluate": "Вычислить выражение",
|
||||
"cmd.face_delete": "Удалить грань",
|
||||
"cmd.file.export_3mf": "Экспортировать 3MF",
|
||||
"cmd.file.export_obj": "Экспортировать OBJ",
|
||||
"cmd.file.export_step": "Экспортировать STEP",
|
||||
"cmd.file.export_stl": "Экспортировать STL",
|
||||
"cmd.file.export_urdf": "Экспортировать URDF",
|
||||
"cmd.file_export": "Экспорт",
|
||||
"cmd.file_insert_mesh": "Вставить меш",
|
||||
"cmd.file_insert_step": "Вставить STEP",
|
||||
@@ -944,6 +956,7 @@
|
||||
"cmd.inspect_colored_components": "Включить цвета компонентов",
|
||||
"cmd.inspect_colored_components_disable": "Отключить цвета компонентов",
|
||||
"cmd.inspect_compare": "Сравнить тела",
|
||||
"cmd.inspect_compare_picture": "Сравнить с изображением",
|
||||
"cmd.inspect_continuity": "Анализ непрерывности",
|
||||
"cmd.inspect_curvature_comb": "Анализ кривизны (гребёнка)",
|
||||
"cmd.inspect_curvature_map": "Карта кривизны",
|
||||
@@ -1030,6 +1043,9 @@
|
||||
"cmd.plastic_rest": "Опора",
|
||||
"cmd.plastic_rules": "Список правил пластика",
|
||||
"cmd.plastic_snap_fit": "Защёлка",
|
||||
"cmd.render.compare.divider": "Переместить разделитель сравнения рендеров",
|
||||
"cmd.render.result.discard": "Отбросить результат рендера",
|
||||
"cmd.render.result.select": "Выбрать результат рендера",
|
||||
"cmd.render_aging_describe": "Многослойное старение поверхности",
|
||||
"cmd.render_backends": "Рендер-бэкенды",
|
||||
"cmd.render_batch": "Рендер всех кадров",
|
||||
@@ -1239,6 +1255,7 @@
|
||||
"cmd.timeline_rollback": "Сдвинуть маркер шкалы",
|
||||
"cmd.timeline_stop": "Остановить воспроизведение истории",
|
||||
"cmd.timeline_suppress": "Подавить операцию",
|
||||
"cmd.ui.assistant": "Показать/скрыть ассистента",
|
||||
"cmd.view.render_browser": "Браузер рендера",
|
||||
"cmd.view.render_dock": "Переместить панель рендера",
|
||||
"cmd.view.render_focus": "Режим фокуса рендера",
|
||||
@@ -1273,9 +1290,11 @@
|
||||
"cmd.view_tool": "Инструмент навигации",
|
||||
"cmd.view_top": "Вид сверху",
|
||||
"cmd.view_visual_style": "Визуальный стиль",
|
||||
"color.blue": "Синий",
|
||||
"color.copy": "Щёлкните, чтобы скопировать значения цвета",
|
||||
"color.edit": "Щёлкните, чтобы изменить цвет",
|
||||
"color.gamma": "Значения цвета показаны в гамма-пространстве 0–255",
|
||||
"color.green": "Зелёный",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "Введите #rrggbb или #rrggbbaa; при вводе шести цифр непрозрачность сохраняется",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1283,6 +1302,7 @@
|
||||
"color.linear": "Значения цвета показаны в линейном пространстве 0–1",
|
||||
"color.recent": "Недавние цвета",
|
||||
"color.recent_help": "Цвета, выбранные в любом поле цвета, появляются здесь",
|
||||
"color.red": "Красный",
|
||||
"color.saturation": "Насыщенность (в процентах)",
|
||||
"color.saturation_value": "Насыщенность и яркость",
|
||||
"color.selected": "Выбранный цвет",
|
||||
@@ -1290,6 +1310,8 @@
|
||||
"color.step_up": "Увеличить на шаг",
|
||||
"color.value": "Значение, то есть яркость (в процентах)",
|
||||
"config.column_filter_empty": "Нет подходящих столбцов",
|
||||
"config.rule_parameter_columns": "Показаны только параметры, у которых есть столбец в таблице. Добавьте столбец кнопкой «Добавить столбец» (+), чтобы использовать параметр здесь.",
|
||||
"config.rule_suppression_columns": "Показаны только операции, у которых в таблице есть столбец «Подавить». Добавьте столбец кнопкой «Добавить столбец» (+), чтобы управлять операцией здесь.",
|
||||
"contact.components_count": "Компонентов: {count}",
|
||||
"contact.enabled_label": "включено",
|
||||
"coupling.gear_1": "Шестерня 1",
|
||||
@@ -1337,7 +1359,11 @@
|
||||
"dialog.pattern_sketch_not_object": "Выберите тела или операции: эскиз — ни то, ни другое. Повторите его кривые собственным инструментом «Массив» или «Зеркало» в эскизе либо сначала выдавите его.",
|
||||
"dialog.select": "↖ Выбрать",
|
||||
"dialog.selected_count": "↖ Выбрано: {count}",
|
||||
"dialog.selection_constraint": "Ограничение {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · кривая {id}",
|
||||
"dialog.selection_guidance": "Щёлкните поле ввода, чтобы изменить его. Щелчок по выделенному объекту убирает его; Ctrl/Cmd+щелчок тоже переключает. Проверьте предпросмотр и нажмите OK.",
|
||||
"dialog.selection_midpoint": "Середина · {id}",
|
||||
"dialog.selection_profile": "{sketch} · профиль {number}",
|
||||
"dialog.selection_step": "{step} из {total}: Выберите {input}",
|
||||
"dialog.step": "Шаг",
|
||||
"document.changed_on_disk": "Файл {name} изменён на диске",
|
||||
@@ -1420,6 +1446,7 @@
|
||||
"help.construct.axis.two_points": "Создаёт вспомогательную ось через две точки, вершины или точки эскиза. Используйте для оси, идущей между двумя известными положениями.",
|
||||
"help.construct.plane.along_path": "Создаёт вспомогательную плоскость, перпендикулярную ребру или кривой, в выбранном месте вдоль неё. Используйте, чтобы нарисовать профиль для операции «Сдвиг» или сечение траектории.",
|
||||
"help.construct.plane.at_angle": "Создаёт вспомогательную плоскость, повёрнутую на угол от плоскости или грани вокруг оси или ребра. Используйте для наклонных эскизов, например для кронштейна под углом.",
|
||||
"help.construct.plane.axis_point": "Создаёт вспомогательную плоскость, содержащую ось и точку вне этой оси. Выберите ось начала координат или вспомогательную ось, прямое ребро или линию эскиза, затем вершину, точку эскиза или вспомогательную точку. Точка на оси не задаёт единственную плоскость.",
|
||||
"help.construct.plane.midplane": "Создаёт вспомогательную плоскость посередине между двумя плоскостями или плоскими гранями либо делящую пополам угол между ними. Используйте как плоскость симметрии для зеркального отражения.",
|
||||
"help.construct.plane.offset": "Создаёт вспомогательную плоскость, параллельную плоскости или плоской грани, на заданном расстоянии или через точку. Используйте, чтобы рисовать на заданной высоте над гранью.",
|
||||
"help.construct.plane.perpendicular": "Создаёт вспомогательную плоскость, содержащую линию или ребро и перпендикулярную плоскости или грани. Используйте, чтобы рисовать на модели вертикально вдоль ребра.",
|
||||
@@ -1553,6 +1580,16 @@
|
||||
"help.sketch.constraint.symmetry": "Делает две точки или кривые зеркальными друг для друга относительно линии или центральной точки. Используйте, чтобы профиль оставался симметричным при изменении одной стороны.",
|
||||
"help.sketch.constraint.tangent": "Заставляет две кривые касаться, например линию и дугу. Используйте для плавных стыков без излома.",
|
||||
"help.sketch.construction": "Переключает кривые между обычной и вспомогательной геометрией, которую профили не учитывают. Используйте вспомогательные кривые как помощники для разметки, ориентиры и размеры.",
|
||||
"help.sketch.construction_line": "Создайте бесконечную вспомогательную прямую через две точки. С её помощью можно разметить эскиз, не разделяя профили.",
|
||||
"help.sketch.construction_line.angle": "Создайте бесконечную вспомогательную прямую под углом к оси X эскиза. Укажите точку и введите угол либо задайте направление.",
|
||||
"help.sketch.construction_line.bisector": "Выберите две пересекающиеся прямые эскиза, затем одну из двух биссектрис. Результат изменяется вместе с исходными прямыми.",
|
||||
"help.sketch.construction_line.horizontal": "Создайте бесконечную горизонтальную вспомогательную прямую через точку. Её горизонтальное ограничение сохраняется при последующих изменениях.",
|
||||
"help.sketch.construction_line.parallel": "Выберите прямую эскиза, затем разместите параллельную бесконечную вспомогательную прямую. Щелчок правой кнопкой — выбрать другую исходную прямую.",
|
||||
"help.sketch.construction_line.perpendicular": "Выберите прямую эскиза, затем разместите перпендикулярную бесконечную вспомогательную прямую. Щелчок правой кнопкой — выбрать другую исходную прямую.",
|
||||
"help.sketch.construction_line.tangent_at": "Создайте касательную в точке окружности или дуги окружности либо в конечной точке открытого сплайна или коники. Выберите кривую, затем точку касания.",
|
||||
"help.sketch.construction_line.tangent_external": "Выберите окружность или дугу, затем внешнюю точку и решение для касательной. В предпросмотре показаны только точки касания на нарисованной дуге.",
|
||||
"help.sketch.construction_line.tangent_two": "Выберите две окружности или дуги, затем в предпросмотре выберите общую касательную. Точки касания должны лежать на нарисованных кривых.",
|
||||
"help.sketch.construction_line.vertical": "Создайте бесконечную вертикальную вспомогательную прямую через точку. Её вертикальное ограничение сохраняется при последующих изменениях.",
|
||||
"help.sketch.copy": "Копирует выбранные кривые и точки эскиза вместе с ограничениями и размерами между ними. Вставьте их в этот эскиз или в другой.",
|
||||
"help.sketch.create": "Начинает новый 2D-эскиз на плоскости или плоской грани модели. Каждое «Выдавливание», «Вращение» и «Сдвиг» начинается с эскиза.",
|
||||
"help.sketch.cut": "Копирует выбранную геометрию эскиза и удаляет её из эскиза одним шагом отмены. Вставьте её в другое место этого эскиза или в другой эскиз.",
|
||||
@@ -1783,6 +1820,8 @@
|
||||
"joint_move.blocked_hint": "Проверьте тип сопряжения (например, «Жёсткое»), закрепление, ограничения сопряжения и значения, заданные параметрами.",
|
||||
"joint_move.blocked_title": "Эту деталь нельзя переместить",
|
||||
"joint_move.coarse": "Шаг поворота вблизи оси (°)",
|
||||
"joint_move.conflict": "Одно из сопряжений этой детали конфликтует с остальными: {joint}.",
|
||||
"joint_move.conflict_hint": "Измените, подавите или удалите конфликтующее сопряжение, чтобы остальные сопряжения могли расположить деталь.",
|
||||
"joint_move.distance": "Расстояние от начала",
|
||||
"joint_move.done": "Готово",
|
||||
"joint_move.fine": "Шаг поворота вдали от оси (°)",
|
||||
@@ -1882,6 +1921,8 @@
|
||||
"motion.track_name": "Дорожка {number}",
|
||||
"nav.alias.help": "Shift+Alt+перетаскивание левой кнопкой вращает, Shift+Alt+средней панорамирует, Shift+Alt+правой меняет масштаб; перетаскивание средней кнопкой тоже панорамирует, правой — вращает",
|
||||
"nav.blender.help": "Перетаскивание средней кнопкой вращает, Shift+средняя панорамирует, Ctrl+средняя меняет масштаб; перетаскивание правой кнопкой камеру не двигает",
|
||||
"nav.creo.help": "Перетаскивание средней кнопкой вращает, Shift+средняя панорамирует, Ctrl+перетаскивание средней кнопкой по вертикали меняет масштаб (вниз — приблизить); колесо прокрутки тоже меняет масштаб",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "Мышь и 3D-мышь вращают вид через оба полюса и сохраняют крен. Привязка к осям по Shift отключена.",
|
||||
"nav.free_orbit.label": "Свободная орбита",
|
||||
"nav.free_orbit.snap_help": "Мышь и 3D-мышь вращают вид через оба полюса и сохраняют крен. Удерживайте Shift при вращении, чтобы привязать направление вида и крен к осям.",
|
||||
@@ -2034,12 +2075,19 @@
|
||||
"parts.family.button_head_cap_screw": "Винт с полукруглой головкой и внутренним шестигранником",
|
||||
"parts.family.dowel_pin": "Цилиндрический штифт",
|
||||
"parts.family.hex_bolt": "Болт / винт с шестигранной головкой",
|
||||
"parts.family.hex_cap_screw_inch": "Винт с шестигранной головкой (дюймовый)",
|
||||
"parts.family.hex_nut": "Шестигранная гайка",
|
||||
"parts.family.hex_nut_inch": "Шестигранная гайка (дюймовая)",
|
||||
"parts.family.set_screw": "Установочный винт с внутренним шестигранником и плоским концом",
|
||||
"parts.family.socket_head_cap_screw": "Винт с цилиндрической головкой и внутренним шестигранником",
|
||||
"parts.family.washer": "Плоская шайба",
|
||||
"parts.family.washer_sae": "Плоская шайба, SAE (узкая)",
|
||||
"parts.family.washer_uss": "Плоская шайба, USS (широкая)",
|
||||
"parts.holes": "Отверстия (необязательно)",
|
||||
"parts.placement_hint": "выберите одно или несколько отверстий, чтобы посадить деталь в каждое, или OK поместит её в начало координат",
|
||||
"parts.system": "Система единиц",
|
||||
"parts.system.inch": "Дюймовая",
|
||||
"parts.system.metric": "Метрическая",
|
||||
"pattern.alignment_2": "Выравнивание 2",
|
||||
"pattern.alignment_hint": "Количество включает оригинал. Центрирование распределяет копии по обе стороны; при чётном количестве смена направления выбирает сторону с дополнительной копией.",
|
||||
"pattern.direction_1": "Направление 1",
|
||||
@@ -2088,6 +2136,8 @@
|
||||
"prefs.bodies_folders_several": "Только если тел несколько",
|
||||
"prefs.bottom": "Снизу",
|
||||
"prefs.browser": "Браузер",
|
||||
"prefs.browser_components_open": "Компоненты развёрнуты с начала",
|
||||
"prefs.browser_components_open_note": "Каждый компонент в браузере сразу развёрнут, как раньше, а не свёрнут, пока вы его не развернёте.",
|
||||
"prefs.browser_eye_left": "Глаз рядом со стрелкой свёртывания",
|
||||
"prefs.browser_eye_left_note": "Строки браузера показывают глаз показа/скрытия рядом со стрелкой свёртывания, как раньше, а не в столбце на правом конце строк.",
|
||||
"prefs.browser_reveal_on_hover": "Появляется при наведении",
|
||||
@@ -2097,6 +2147,8 @@
|
||||
"prefs.button_left": "Перетаскивание левой кнопкой",
|
||||
"prefs.button_middle": "Перетаскивание средней кнопкой",
|
||||
"prefs.button_right": "Перетаскивание правой кнопкой",
|
||||
"prefs.carousel_next": "Далее",
|
||||
"prefs.carousel_previous": "Назад",
|
||||
"prefs.choose_font": "Выбрать файл шрифта…",
|
||||
"prefs.click": "Щелчок",
|
||||
"prefs.contact_shading": "Мягкое контактное затенение",
|
||||
@@ -2109,6 +2161,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Криволинейные поверхности",
|
||||
"prefs.curves": "Криволинейные поверхности",
|
||||
"prefs.cut_preview_tint": "Показывать удаляемый материал красным",
|
||||
"prefs.cut_preview_tint_help": "В предпросмотре материал, который удаляет операция, и форма, которую она заменяет, закрашиваются красным полупрозрачным призраком. Если выключено, эти заливки полностью прозрачны и рисуются только их контуры.",
|
||||
"prefs.dark_parts_shading": "Сохранять форму тёмных деталей",
|
||||
"prefs.dark_parts_shading_help": "Затенять вид в линейном свете с мягкой заполняющей подсветкой неба, чтобы тёмные грани сохраняли градиент, а не становились чёрными",
|
||||
"prefs.dead_zone_note": "Заштрихованная полоса — мёртвая зона: движение внутри неё игнорируется.",
|
||||
@@ -2166,6 +2220,8 @@
|
||||
"prefs.interface_language": "Язык интерфейса",
|
||||
"prefs.interface_note": "Как выглядит SolidCraft и где расположены его части.",
|
||||
"prefs.interval_minutes": "{n} мин",
|
||||
"prefs.invert_curved_roll_arrows": "Инвертировать изогнутые стрелки крена",
|
||||
"prefs.invert_curved_roll_arrows_note": "Две изогнутые стрелки в углу куба поворачивают вид в плоскости экрана. Вкл.: стрелка по часовой стрелке поворачивает вид против часовой, а стрелка против часовой — по часовой. Выкл.: каждая стрелка поворачивает вид в ту сторону, куда она изогнута.",
|
||||
"prefs.invert_navigator_arrows": "Инвертировать стрелки вращения куба видов",
|
||||
"prefs.invert_navigator_arrows_note": "Стрелки вокруг куба вращают камеру в ту сторону, куда указывают, как в Fusion и Inventor: стрелка слева указывает вправо и поворачивает камеру вправо. Выкл.: стрелка поворачивает куб в сторону, где она расположена, поэтому левая стрелка поворачивает камеру влево. Изогнутые стрелки крена не затрагиваются.",
|
||||
"prefs.keep_work_on_tab_switch": "Сохранять открытые диалоги и инструменты при переключении между дизайнами",
|
||||
@@ -2219,10 +2275,14 @@
|
||||
"prefs.overall_speed": "Общая скорость",
|
||||
"prefs.parameter_list_rows": "Строк в списке параметров",
|
||||
"prefs.parameter_list_rows_tip": "Стрелка вниз в пустом поле значения открывает список параметров, сначала избранные, не больше этого числа. Меньшее число делает список лёгким в большом дизайне.",
|
||||
"prefs.persistent_ribbon_pressed": "Подсвечивать активные команды на ленте",
|
||||
"prefs.persistent_ribbon_pressed_help": "Подсвечивает команды на ленте, пока их действие включено. Отключите, чтобы сохранить прежний нейтральный вид и справку по командам.",
|
||||
"prefs.perspective_fov": "Угол обзора",
|
||||
"prefs.placement": "Размещение",
|
||||
"prefs.prefer_free_sketch_points": "Предпочитать подвижные точки эскиза, когда точки перекрываются",
|
||||
"prefs.preview_choices": "Предпросмотр вашего выбора",
|
||||
"prefs.preview_tint_new_faces": "Подкрашивать новую поверхность в предпросмотре",
|
||||
"prefs.preview_tint_new_faces_help": "Предпросмотр операции, изменяющей тело, может рисовать добавляемую ею поверхность цветом предпросмотра. Если выключено, тело рисуется как есть.",
|
||||
"prefs.print_face_highlight": "Подсветка грани печати",
|
||||
"prefs.print_face_highlight_tip": "Насколько сильно грань печати тела окрашивается и обводится в виде. 0 % — без подсветки.",
|
||||
"prefs.profile_active": "Активный профиль",
|
||||
@@ -2282,6 +2342,8 @@
|
||||
"prefs.sketch_exit_view_note": "Куда переходит вид после «Завершить эскиз», «Отмена» или двойного Esc.",
|
||||
"prefs.sketch_face_activates_part": "Эскиз на грани детали делает эту деталь активной",
|
||||
"prefs.sketch_face_activates_part_note": "Эскиз, начатый на грани тела другого компонента, сначала активирует этот компонент, поэтому эскиз принадлежит ему и в браузере остаётся с этой деталью. После этого деталь остаётся активной. Выкл.: эскиз принадлежит активному компоненту.",
|
||||
"prefs.sketch_open_look_at": "Поворачивать камеру к эскизу при его открытии",
|
||||
"prefs.sketch_open_look_at_note": "Выкл.: при создании или редактировании эскиза вид не меняется. «Взгляд на» поворачивает вид к эскизу, когда это нужно.",
|
||||
"prefs.sketch_recent_snap": "Автопривязка к недавним центрам и серединам",
|
||||
"prefs.sketch_recent_snap_note": "При рисовании курсор привязывается к месту пересечения направляющих двух последних центров окружностей или середин прямых отрезков. Выкл.: эти направляющие остаются только визуальными, а курсор не смещается.",
|
||||
"prefs.sketch_selection": "Эскиз и выделение",
|
||||
@@ -2389,6 +2451,10 @@
|
||||
"properties.title": "Свойства: {name}",
|
||||
"recovery.days_ago": "{count} дн. назад",
|
||||
"recovery.detail": "{file} · операций: {features} · автосохранение {when}",
|
||||
"recovery.discard_all": "Отбросить все",
|
||||
"recovery.discard_all_detail": "Это действие нельзя отменить.",
|
||||
"recovery.discard_all_title": "Отбросить автосохранения ({count})?",
|
||||
"recovery.discard_all_title_one": "Отбросить 1 автосохранение?",
|
||||
"recovery.hours_ago": "{count} ч назад",
|
||||
"recovery.just_now": "только что",
|
||||
"recovery.min_ago": "{count} мин назад",
|
||||
@@ -2504,6 +2570,10 @@
|
||||
"render.base_color": "Базовый цвет · sRGB",
|
||||
"render.base_metallic": "Металличность",
|
||||
"render.base_opaque_only": "Этот редактор сохраняет семантику непрозрачного тела. Для оптического пропускания света используйте отдельные элементы управления стеклом.",
|
||||
"render.batch_all_shots": "Все кадры ({count})",
|
||||
"render.batch_empty": "Сохраните кадр, прежде чем рендерить все кадры.",
|
||||
"render.batch_limit": "Рендер всех кадров ограничен 32 кадрами.",
|
||||
"render.batch_status": "Выполняется: {running} · ожидает: {waiting}",
|
||||
"render.bloom_enable": "Показывать свечение",
|
||||
"render.bloom_help": "Творческое свечение только на выводе от положительных линейных ярких участков, размытое тремя нормированными блочными проходами. Радиус задан в пикселях вывода. Добавляется в линейном свете дисплея до sRGB; это не эффект объектива, не объёмный эффект и не улучшение сходимости. Необработанный EXR не меняется.",
|
||||
"render.bloom_opaque": "Свечение требует непрозрачного вывода. Прозрачный PNG отклоняется, а не молча сводится к непрозрачному изображению.",
|
||||
@@ -2611,6 +2681,13 @@
|
||||
"render.choose_look": "Сначала выберите внешний вид в разделе «Внешние виды»",
|
||||
"render.chromatic_offset": "Хроматическое смещение краёв",
|
||||
"render.clear_stage": "Удалить сохранённую площадку",
|
||||
"render.compare_divider": "Разделитель сравнения",
|
||||
"render.compare_live": "Интерактивный",
|
||||
"render.compare_live_preview": "Интерактивный просмотр",
|
||||
"render.compare_render": "Рендер {number}",
|
||||
"render.compare_side": "Рендер {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} с · {renderer} · зафиксировано в редакции {revision}",
|
||||
"render.compare_swap": "Поменять местами",
|
||||
"render.copy_request": "Копировать JSON",
|
||||
"render.cpu_aa_help": "Пространственные сэмплы: чем их больше, тем лучше края и тем дольше рендер.",
|
||||
"render.cpu_area_add": "Добавить прямоугольный источник света CPU",
|
||||
@@ -3329,6 +3406,10 @@
|
||||
"render.result_retained": "Показано последнее готовое изображение; новый рендер его пока не заменил.",
|
||||
"render.result_sequence": "Контрольная точка",
|
||||
"render.result_title": "Результат рендера",
|
||||
"render.results_choose": "Сохранённые результаты",
|
||||
"render.results_label": "Рендер {number} · {shot} · {seconds} с",
|
||||
"render.results_stale_label": "{result} [устарел]",
|
||||
"render.results_unnamed": "Изображение",
|
||||
"render.return_design": "Вернуться к проектированию",
|
||||
"render.review_stale": "Этот обзор устарел. Повторите определение и проверку перед применением.",
|
||||
"render.ribbon_aging": "Старение",
|
||||
@@ -3804,10 +3885,7 @@
|
||||
"the two origins are on the same component": "обе точки сопряжения находятся на одном компоненте",
|
||||
"thread.automatic_size": "Автоматический размер",
|
||||
"thread.designation": "Обозначение",
|
||||
"thread.direction": "Направление резьбы",
|
||||
"thread.iso_metric": "Метрическая ISO",
|
||||
"thread.left_hand": "Левая",
|
||||
"thread.right_hand": "Правая",
|
||||
"thread.select_size": "Выберите размер",
|
||||
"timeline.delete_after_marker_one": "Удалить 1 операцию после маркера истории",
|
||||
"timeline.delete_after_marker_other": "Удалить операции после маркера истории: {count}",
|
||||
@@ -3979,6 +4057,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Выполните это в терминале, затем снова нажмите «Подключить»:",
|
||||
"ui.agent_chat_auth_title": "Вход в агент",
|
||||
"ui.agent_chat_cancel": "Отмена",
|
||||
"ui.agent_chat_cancel_timeout": "Агент не ответил на «Остановить» и был отключён. Подключитесь снова, чтобы продолжить.",
|
||||
"ui.agent_chat_cannot_start": "Установлен, но не запускается: {error}",
|
||||
"ui.agent_chat_changes": "Изменений: {n}, один шаг отмены",
|
||||
"ui.agent_chat_changes_n": "Изменений: {n}",
|
||||
@@ -3988,11 +4067,13 @@
|
||||
"ui.agent_chat_command": "Команда агента",
|
||||
"ui.agent_chat_connect": "Подключить",
|
||||
"ui.agent_chat_copy": "Копировать",
|
||||
"ui.agent_chat_copy_message": "Копировать сообщение",
|
||||
"ui.agent_chat_ctx_bodies": "Тела",
|
||||
"ui.agent_chat_ctx_elements": "Элементы",
|
||||
"ui.agent_chat_ctx_features": "Операции",
|
||||
"ui.agent_chat_ctx_more": "+ещё {n}",
|
||||
"ui.agent_chat_ctx_other": "Другое",
|
||||
"ui.agent_chat_custom_heading": "Своя модель",
|
||||
"ui.agent_chat_custom_hint": "Любая программа, поддерживающая Agent Client Protocol через stdin и stdout. Оболочка для запуска не используется.",
|
||||
"ui.agent_chat_denied": "Отклонено",
|
||||
"ui.agent_chat_deny": "Отклонить",
|
||||
@@ -4036,8 +4117,16 @@
|
||||
"ui.agent_chat_mode_all": "Всё",
|
||||
"ui.agent_chat_mode_ask": "Спросить",
|
||||
"ui.agent_chat_mode_edits": "Правки",
|
||||
"ui.agent_chat_model_custom": "Использовать свою модель: {model}",
|
||||
"ui.agent_chat_model_no_tools": "{model} не может использовать инструменты SolidCraft, поэтому может только вести чат. Выберите модель с поддержкой инструментов.",
|
||||
"ui.agent_chat_model_rejected": "Агент не принял {model}: {error}",
|
||||
"ui.agent_chat_model_search": "Поиск моделей…",
|
||||
"ui.agent_chat_model_tip": "Модель, которую использует агент. Ваш выбор запоминается для этого агента.",
|
||||
"ui.agent_chat_models_failed": "Не удалось получить список моделей: {error}",
|
||||
"ui.agent_chat_models_heading": "Доступные модели",
|
||||
"ui.agent_chat_models_listing": "Получение списка моделей…",
|
||||
"ui.agent_chat_models_none": "Конечная точка не вернула ни одной модели",
|
||||
"ui.agent_chat_models_not_a_list": "В ответе нет списка моделей",
|
||||
"ui.agent_chat_new": "Новый чат",
|
||||
"ui.agent_chat_new_confirm": "Начать новый чат? Текущий будет очищен.",
|
||||
"ui.agent_chat_next_picture": "Следующее изображение",
|
||||
@@ -4309,14 +4398,21 @@
|
||||
"ui.construction.contact_hint": "Касательная: выберите окружность/дугу или открытый сплайн/конику, затем точку касания (для сплайна/коники — конец)",
|
||||
"ui.construction.external_hint": "Касательная: выберите окружность или дугу, внешнюю точку, затем нужное решение",
|
||||
"ui.construction.free_hint": "Вспомогательная прямая: укажите две точки; правая кнопка — начать заново",
|
||||
"ui.construction.normal_needs_length": "Бесконечная прямая всегда вспомогательная: введите длину, чтобы нарисовать обычную линию",
|
||||
"ui.construction.point_hint": "Вспомогательная прямая: укажите точку прохождения",
|
||||
"ui.construction.reference_hint": "Выберите прямую, затем точку; правая кнопка — сменить исходную прямую",
|
||||
"ui.construction.ribbon_label": "Всп. линии",
|
||||
"ui.construction.side_hint": "Вспомогательная прямая: укажите сторону, в которую идёт прямая; правая кнопка — начать заново",
|
||||
"ui.context_menu": "Контекстное меню",
|
||||
"ui.continuity_crease": "Излом",
|
||||
"ui.continuity_curvature": "Кривизна",
|
||||
"ui.continuity_curvature_change": "Изменение кривизны",
|
||||
"ui.continuity_g0": "G0, излом",
|
||||
"ui.continuity_g1": "G1, касательная",
|
||||
"ui.continuity_g2": "G2, кривизна",
|
||||
"ui.continuity_gap": "Зазор",
|
||||
"ui.continuity_position": "Положение",
|
||||
"ui.continuity_tangent": "Касание",
|
||||
"ui.copy": "Копировать",
|
||||
"ui.corner": "Угол",
|
||||
"ui.corners": "Углы",
|
||||
@@ -4577,6 +4673,7 @@
|
||||
"ui.hideOrigin": "Скрыть начало координат",
|
||||
"ui.hidePlanes": "Скрыть плоскости",
|
||||
"ui.hideProfile": "Скрыть профиль",
|
||||
"ui.hide_component_history": "Скрыть его историю",
|
||||
"ui.hide_origin_by_default": "Скрывать начало координат по умолчанию",
|
||||
"ui.hide_origin_note": "Новые дизайны создаются со скрытой папкой «Начало координат». Как только вы покажете или скроете её, дизайн запоминает собственную настройку.",
|
||||
"ui.history_back": "На один шаг назад",
|
||||
@@ -4584,6 +4681,7 @@
|
||||
"ui.history_edge_hint": "двойной щелчок по границе",
|
||||
"ui.history_first": "Перейти в начало",
|
||||
"ui.history_forward": "На один шаг вперёд",
|
||||
"ui.history_hide_hidden": "Скрыть скрытые компоненты",
|
||||
"ui.history_hide_names": "Скрыть имена",
|
||||
"ui.history_last": "Перейти в конец",
|
||||
"ui.history_marker_tip": "Маркер истории: перетащите, чтобы откатить модель; новые операции добавляются сюда",
|
||||
@@ -4593,10 +4691,12 @@
|
||||
"ui.history_play_button_show": "Показать кнопку воспроизведения",
|
||||
"ui.history_play_finish_action": "Завершите текущее действие перед воспроизведением истории",
|
||||
"ui.history_play_roll_back": "Переместите маркер истории назад перед воспроизведением",
|
||||
"ui.history_show_hidden": "Показать скрытые компоненты",
|
||||
"ui.history_show_names": "Показать имена",
|
||||
"ui.history_sketch_listed_under": "Показан под операцией {feature}",
|
||||
"ui.history_title": "История",
|
||||
"ui.history_toggle_names": "Показать или скрыть имена операций",
|
||||
"ui.history_waits_for_editor": "«Отменить», «Повторить» и «История» ждут, пока откроется редактор операции",
|
||||
"ui.hole": "Отверстие",
|
||||
"ui.hole_tap_type": "Тип резьбы отверстия",
|
||||
"ui.hole_type": "Тип отверстия",
|
||||
@@ -4612,6 +4712,7 @@
|
||||
"ui.insert_design.saved_file": "В {name} есть несохранённые изменения, поэтому вставлен его сохранённый файл. Сохраните его, затем выполните «Обновить связи», чтобы подтянуть изменения.",
|
||||
"ui.insert_design.unsaved": "Несохранённые изменения",
|
||||
"ui.inspect": "Инспекция",
|
||||
"ui.instances": "Экземпляры",
|
||||
"ui.intersect": "Пересечение",
|
||||
"ui.isolate": "Изолировать",
|
||||
"ui.join": "Объединить",
|
||||
@@ -4865,6 +4966,7 @@
|
||||
"ui.showDimension": "Показать размер",
|
||||
"ui.showOrigin": "Показать начало координат",
|
||||
"ui.showPlanes": "Показать плоскости",
|
||||
"ui.show_component_history": "Показать его историю",
|
||||
"ui.show_constraints": "Ограничения",
|
||||
"ui.show_construction_geometries": "Вспомогательная геометрия",
|
||||
"ui.show_dimensions": "Размеры",
|
||||
@@ -5047,7 +5149,7 @@
|
||||
"update.check_unavailable": "Проверка обновлений недоступна для этой установки.",
|
||||
"update.checking": "Проверка…",
|
||||
"update.copy": "Копировать",
|
||||
"update.dmg_opened": "Образ диска открыт и проверен: перетащите SolidCraft в «Программы», заменив старую копию, затем перезапустите SolidCraft.",
|
||||
"update.dmg_opened": "Образ диска открыт и проверен: завершите SolidCraft, перетащите новый SolidCraft в «Программы», заменив старую копию, затем снова откройте SolidCraft.",
|
||||
"update.download": "Скачать",
|
||||
"update.download_open": "Скачать и открыть",
|
||||
"update.downloading": "Загрузка и проверка… {done} из {total} МБ",
|
||||
@@ -5089,6 +5191,7 @@
|
||||
"web.open_link": "Открыть связанный дизайн?",
|
||||
"web.open_link_confirm": "Есть несохранённые изменения. Открыть связанный дизайн в новой вкладке?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ выбрать · Enter / Tab дополнить · Esc закрыть",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ выбрать · Enter — применить это значение",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ выбрать · Enter применяет введённое · Esc закрыть",
|
||||
"⌘Enter continues on GitHub.": "⌘Enter продолжит на GitHub."
|
||||
}
|
||||
|
||||
+78
-3
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "Правоугаони",
|
||||
"Rectangular Tube": "Правоугаона цев",
|
||||
"Reference face": "Референтно лице",
|
||||
"Reference plane": "Референтна раван",
|
||||
"Relief Depth": "Дубина растерећења",
|
||||
"Relief Width": "Ширина растерећења",
|
||||
"Remove Tools": "Уклони алате",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "Пресек",
|
||||
"Section (optional)": "Пресек (необавезно)",
|
||||
"Section position": "Положај пресека",
|
||||
"Sections": "Пресеци",
|
||||
"Sections (one sketch each)": "Пресеци (по једна скица)",
|
||||
"Select Other": "Изабери друго",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Изабери друго: држите лево дугме мирно на моделу (или десни клик › Изабери друго…) да прикажете све испод показивача; поново кликните на исто место да изаберете следећи елемент. Двапут кликните средњим дугметом за Почетни приказ (Подешавања › Навигација).",
|
||||
@@ -859,6 +861,7 @@
|
||||
"cmd.component_extract": "Издвоји у датотеку",
|
||||
"cmd.component_finish_edit": "Заврши измену",
|
||||
"cmd.component_from_bodies": "Направи компоненте из тела",
|
||||
"cmd.component_hide_history": "Сакриј временску траку компоненте",
|
||||
"cmd.component_insert_design": "Уметни дизајн",
|
||||
"cmd.component_links": "Списак веза",
|
||||
"cmd.component_list": "Списак компоненти",
|
||||
@@ -875,6 +878,7 @@
|
||||
"cmd.component_rename": "Преименуј компоненту",
|
||||
"cmd.component_replace_with_link": "Замени везом",
|
||||
"cmd.component_revert_position": "Врати положај",
|
||||
"cmd.component_show_history": "Прикажи временску траку компоненте",
|
||||
"cmd.component_unlock": "Откључај садржај",
|
||||
"cmd.component_update_links": "Ажурирај везе",
|
||||
"cmd.config_activate": "Активирај конфигурацију",
|
||||
@@ -885,6 +889,7 @@
|
||||
"cmd.config_rule": "Правило конфигурације",
|
||||
"cmd.config_rules": "Правила конфигурације",
|
||||
"cmd.config_table": "Прикажи табелу конфигурација",
|
||||
"cmd.construct.plane.axis_point": "Раван кроз осу и тачку",
|
||||
"cmd.construct_axis_edge": "Оса кроз ивицу",
|
||||
"cmd.construct_axis_normal_to_face": "Оса окомита на лице",
|
||||
"cmd.construct_axis_through_cylinder": "Оса кроз цилиндар/конус/торус",
|
||||
@@ -933,6 +938,11 @@
|
||||
"cmd.explode_show": "Прикажи растављени приказ",
|
||||
"cmd.expr_evaluate": "Израчунај израз",
|
||||
"cmd.face_delete": "Избриши лице",
|
||||
"cmd.file.export_3mf": "Извези 3MF",
|
||||
"cmd.file.export_obj": "Извези OBJ",
|
||||
"cmd.file.export_step": "Извези STEP",
|
||||
"cmd.file.export_stl": "Извези STL",
|
||||
"cmd.file.export_urdf": "Извези URDF",
|
||||
"cmd.file_export": "Извоз",
|
||||
"cmd.file_insert_mesh": "Уметни мрежу",
|
||||
"cmd.file_insert_step": "Уметни STEP",
|
||||
@@ -950,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "Омогући боје компоненти",
|
||||
"cmd.inspect_colored_components_disable": "Онемогући боје компоненти",
|
||||
"cmd.inspect_compare": "Упореди тела",
|
||||
"cmd.inspect_compare_picture": "Упореди слику",
|
||||
"cmd.inspect_continuity": "Анализа континуитета",
|
||||
"cmd.inspect_curvature_comb": "Анализа чешља закривљености",
|
||||
"cmd.inspect_curvature_map": "Анализа мапе закривљености",
|
||||
@@ -1040,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "Измени облак тачака",
|
||||
"cmd.pointcloud_insert": "Уметни облак тачака",
|
||||
"cmd.pointcloud_list": "Облаци тачака",
|
||||
"cmd.render.compare.divider": "Помери разделник поређења рендеровања",
|
||||
"cmd.render.result.discard": "Одбаци резултат рендеровања",
|
||||
"cmd.render.result.select": "Изабери резултат рендеровања",
|
||||
"cmd.render_aging_describe": "Слојевито старење површине",
|
||||
"cmd.render_backends": "Позадински системи рендеровања",
|
||||
"cmd.render_batch": "Рендеруј све кадрове",
|
||||
@@ -1254,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "Помери ознаку враћања",
|
||||
"cmd.timeline_stop": "Заустави временску траку",
|
||||
"cmd.timeline_suppress": "Потисни операцију",
|
||||
"cmd.ui.assistant": "Прикажи или сакриј Асистента",
|
||||
"cmd.view.render_browser": "Стабло рендеровања",
|
||||
"cmd.view.render_dock": "Премести панел рендеровања",
|
||||
"cmd.view.render_focus": "Фокусни приказ рендеровања",
|
||||
@@ -1288,9 +1303,11 @@
|
||||
"cmd.view_tool": "Алат за навигацију",
|
||||
"cmd.view_top": "Поглед одозго",
|
||||
"cmd.view_visual_style": "Визуелни стил",
|
||||
"color.blue": "Плава",
|
||||
"color.copy": "Кликните да копирате вредности боје",
|
||||
"color.edit": "Кликните да измените боју",
|
||||
"color.gamma": "Приказ вредности боје у гама простору 0-255",
|
||||
"color.green": "Зелена",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "Унесите #rrggbb или #rrggbbaa; шест цифара чува непрозирност",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1298,6 +1315,7 @@
|
||||
"color.linear": "Приказ вредности боје у линеарном простору 0-1",
|
||||
"color.recent": "Недавне боје",
|
||||
"color.recent_help": "Овде се појављују боје које изаберете у било ком пољу боје",
|
||||
"color.red": "Црвена",
|
||||
"color.saturation": "Засићеност (проценат)",
|
||||
"color.saturation_value": "Засићеност и вредност",
|
||||
"color.selected": "Изабрана боја",
|
||||
@@ -1305,6 +1323,8 @@
|
||||
"color.step_up": "Повећај за корак",
|
||||
"color.value": "Вредност, светлина (проценат)",
|
||||
"config.column_filter_empty": "Нема одговарајућих колона",
|
||||
"config.rule_parameter_columns": "Приказани су само параметри који имају колону у табели. Додајте колону помоћу Додај колону (+) да бисте параметар користили овде.",
|
||||
"config.rule_suppression_columns": "Приказане су само операције које имају колону Потисни у табели. Додајте колону помоћу Додај колону (+) да бисте овде могли да управљате операцијом.",
|
||||
"contact.components_count": "Компоненте: {count}",
|
||||
"contact.enabled_label": "омогућено",
|
||||
"coupling.gear_1": "Зупчаник 1",
|
||||
@@ -1353,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "Изаберите тела или операције: скица није ни једно ни друго. Поновите њене криве помоћу алата Низ или Пресликај у самој скици или је прво екструдирајте.",
|
||||
"dialog.select": "↖ Изабери",
|
||||
"dialog.selected_count": "↖ Изабрано: {count}",
|
||||
"dialog.selection_constraint": "Ограничење {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · крива {id}",
|
||||
"dialog.selection_guidance": "Изаберите улаз да га промените. Кликните изабране објекте да их уклоните; Ctrl/Cmd-клик их такође укључује и искључује. Погледајте преглед, а затим притисните У реду.",
|
||||
"dialog.selection_midpoint": "Средишња тачка · {id}",
|
||||
"dialog.selection_profile": "{sketch} · профил {number}",
|
||||
"dialog.selection_step": "{step} од {total}: Изаберите {input}",
|
||||
"dialog.step": "Корак",
|
||||
"document.changed_on_disk": "{name} је промењен на диску",
|
||||
@@ -1436,6 +1460,7 @@
|
||||
"help.construct.axis.two_points": "Прави помоћну осу кроз две тачке, врхове или тачке скице. Користите је за осу која пролази кроз два позната положаја.",
|
||||
"help.construct.plane.along_path": "Прави помоћну раван окомиту на ивицу или криву, на положају дуж ње. Користите је да скицирате профил за повлачење или попречни пресек путање.",
|
||||
"help.construct.plane.at_angle": "Прави помоћну раван заокренуту под углом у односу на раван или лице, око осе или ивице. Користите је за нагнуте скице, на пример за угаони носач.",
|
||||
"help.construct.plane.axis_point": "Прави помоћну раван која садржи осу и тачку изван те осе. Изаберите осу исходишта или помоћну осу, праву ивицу или линију скице, па врх, тачку скице или помоћну тачку. Тачка на оси не може да одреди јединствену раван.",
|
||||
"help.construct.plane.midplane": "Прави помоћну раван на половини између две равни или равна лица, или раван која полови угао под којим се састају. Користите је као раван симетрије за пресликавање.",
|
||||
"help.construct.plane.offset": "Прави помоћну раван паралелну равни или равном лицу, на задатом растојању или кроз тачку. Користите је да скицирате на одређеној висини изнад лица.",
|
||||
"help.construct.plane.perpendicular": "Прави помоћну раван која садржи линију или ивицу и стоји окомито на раван или лице. Користите је да скицирате усправно на моделу дуж ивице.",
|
||||
@@ -1812,6 +1837,8 @@
|
||||
"joint_move.blocked_hint": "Проверите врсту споја (на пример Круто), учвршћивање, границе споја и вредности које покрећу параметри.",
|
||||
"joint_move.blocked_title": "Овај део се не може померити",
|
||||
"joint_move.coarse": "Корак ротације близу осе (°)",
|
||||
"joint_move.conflict": "Један спој овог дела је у сукобу са осталима: {joint}.",
|
||||
"joint_move.conflict_hint": "Измените, потисните или избришите спој који је у сукобу да би остали спојеви могли да поставе део.",
|
||||
"joint_move.distance": "Растојање од почетка",
|
||||
"joint_move.done": "Готово",
|
||||
"joint_move.fine": "Корак ротације далеко од осе (°)",
|
||||
@@ -1911,6 +1938,8 @@
|
||||
"motion.track_name": "Стаза {number}",
|
||||
"nav.alias.help": "Shift+Alt+превлачење левим дугметом орбитира, Shift+Alt+средњим помера, Shift+Alt+десним зумира; превлачење средњим дугметом такође помера, а десним орбитира",
|
||||
"nav.blender.help": "Превлачење средњим дугметом орбитира, Shift+средње помера, Ctrl+средње зумира; превлачење десним дугметом не дира камеру",
|
||||
"nav.creo.help": "Превлачење средњим дугметом орбитира, Shift+средње помера, Ctrl+средње уз вертикално превлачење зумира (надоле за приближавање); точкић такође зумира",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "Миш и 3D миш ротирају кроз оба пола и чувају нагиб. Привлачење оса тастером Shift је искључено.",
|
||||
"nav.free_orbit.label": "Слободно орбитирање",
|
||||
"nav.free_orbit.snap_help": "Миш и 3D миш ротирају кроз оба пола и чувају нагиб. Држите Shift током превлачења за орбитирање да привучете смер погледа и нагиб.",
|
||||
@@ -2063,12 +2092,19 @@
|
||||
"parts.family.button_head_cap_screw": "Завртањ са полуоблом главом и унутрашњим шестоугаоником",
|
||||
"parts.family.dowel_pin": "Паралелни клин (колчић)",
|
||||
"parts.family.hex_bolt": "Завртањ са шестоугаоном главом",
|
||||
"parts.family.hex_cap_screw_inch": "Завртањ са шестоугаоном главом (инч)",
|
||||
"parts.family.hex_nut": "Шестоугаона навртка",
|
||||
"parts.family.hex_nut_inch": "Шестоугаона навртка (инч)",
|
||||
"parts.family.set_screw": "Завртањ без главе са унутрашњим шестоугаоником, равног врха",
|
||||
"parts.family.socket_head_cap_screw": "Завртањ са цилиндричном главом и унутрашњим шестоугаоником",
|
||||
"parts.family.washer": "Обична подлошка",
|
||||
"parts.family.washer_sae": "Обична подлошка, SAE (уска)",
|
||||
"parts.family.washer_uss": "Обична подлошка, USS (широка)",
|
||||
"parts.holes": "Рупе (необавезно)",
|
||||
"parts.placement_hint": "изаберите једну или више рупа да у сваку поставите део, или У реду поставља део у исходиште",
|
||||
"parts.system": "Систем јединица",
|
||||
"parts.system.inch": "Инчни",
|
||||
"parts.system.metric": "Метрички",
|
||||
"pattern.alignment_2": "Поравнање 2",
|
||||
"pattern.alignment_hint": "Број укључује оригинал. Центар равномерно распоређује копије; Обрни бира додатну страну за парне укупне бројеве.",
|
||||
"pattern.direction_1": "Смер 1",
|
||||
@@ -2130,6 +2166,8 @@
|
||||
"prefs.button_left": "Превлачење левим дугметом",
|
||||
"prefs.button_middle": "Превлачење средњим дугметом",
|
||||
"prefs.button_right": "Превлачење десним дугметом",
|
||||
"prefs.carousel_next": "Следеће",
|
||||
"prefs.carousel_previous": "Претходно",
|
||||
"prefs.choose_font": "Изабери датотеку фонта…",
|
||||
"prefs.click": "Клик",
|
||||
"prefs.contact_shading": "Меко сенчење додира",
|
||||
@@ -2142,6 +2180,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Закривљене површи",
|
||||
"prefs.curves": "Закривљене површи",
|
||||
"prefs.cut_preview_tint": "Прикажи уклоњени материјал у црвеној боји",
|
||||
"prefs.cut_preview_tint_help": "Преглед испуњава материјал који операција уклања и облик који замењује црвеном провидном сенком. Када је искључено, те испуне су потпуно провидне и цртају се само њихови обриси.",
|
||||
"prefs.dark_parts_shading": "Задржи облик тамних делова",
|
||||
"prefs.dark_parts_shading_help": "Сенчи приказ у линеарном светлу уз меко допунско светло неба, тако да тамна лица задрже градијент уместо да поцрне",
|
||||
"prefs.dead_zone_note": "Шрафирана трака је мртва зона: кретање унутар ње се занемарује.",
|
||||
@@ -2200,6 +2240,8 @@
|
||||
"prefs.interface_language": "Језик интерфејса",
|
||||
"prefs.interface_note": "Како SolidCraft изгледа и где су његови делови.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_curved_roll_arrows": "Обрни закривљене стрелице за ваљање",
|
||||
"prefs.invert_curved_roll_arrows_note": "Две закривљене стрелице у углу коцке ваљају приказ унутар равни екрана. Укључено: стрелица у смеру казаљке на сату ваља приказ супротно од смера казаљке на сату, а стрелица супротно од смера казаљке на сату ваља га у смеру казаљке на сату. Искључено: свака стрелица ваља приказ онако како је закривљена.",
|
||||
"prefs.invert_navigator_arrows": "Обрни стрелице за ротацију коцке приказа",
|
||||
"prefs.invert_navigator_arrows_note": "Стрелице око коцке орбитирају камеру у смеру у којем показују, као у програмима Fusion и Inventor: стрелица са леве стране показује удесно и закреће камеру удесно. Искључено: стрелица окреће коцку ка страни на којој се налази, па лева стрелица закреће камеру улево. Подешавање не утиче на закривљене стрелице за ваљање.",
|
||||
"prefs.keep_work_on_tab_switch": "Задржи дијалоге и алате отвореним при пребацивању између дизајна",
|
||||
@@ -2259,6 +2301,8 @@
|
||||
"prefs.placement": "Постављање",
|
||||
"prefs.prefer_free_sketch_points": "Дај предност покретним тачкама скице када се тачке преклапају",
|
||||
"prefs.preview_choices": "Преглед ваших избора",
|
||||
"prefs.preview_tint_new_faces": "Обоји нову површ у прегледу",
|
||||
"prefs.preview_tint_new_faces_help": "Преглед операције која мења тело може да нацрта површ коју додаје у боји прегледа. Када је искључено, тело се црта какво јесте.",
|
||||
"prefs.print_face_highlight": "Истицање површине за штампу",
|
||||
"prefs.print_face_highlight_tip": "Колико се јако површина за штампу тела боји и обрубљује у приказу. 0 % не приказује истицање.",
|
||||
"prefs.profile_active": "Активни профил",
|
||||
@@ -2556,6 +2600,10 @@
|
||||
"render.base_color": "Основна боја · sRGB",
|
||||
"render.base_metallic": "Метални удео",
|
||||
"render.base_opaque_only": "Овај уређивач чува семантику непрозирних тела. За оптичко пропуштање користите изричите контроле стакла.",
|
||||
"render.batch_all_shots": "Сви кадрови ({count})",
|
||||
"render.batch_empty": "Сачувајте кадар пре рендеровања свих кадрова.",
|
||||
"render.batch_limit": "Рендеровање свих кадрова ограничено је на 32 кадра.",
|
||||
"render.batch_status": "У току: {running} · На чекању: {waiting}",
|
||||
"render.bloom_enable": "Сјај приказа",
|
||||
"render.bloom_help": "Креативни сјај само на излазу из позитивних линеарних светлих делова, раширен кроз три нормализована пролаза кутијастог филтера. Полупречник је у пикселима излаза. Додаје се у линеарној светлости приказа пре sRGB-а; није објектив, волуметријски ефекат нити побољшање конвергенције. Сирови EXR остаје непромењен.",
|
||||
"render.bloom_opaque": "Сјај захтева непрозирни излаз. Провидни PNG се одбија, а не спљоштава тихо.",
|
||||
@@ -2666,6 +2714,13 @@
|
||||
"render.choose_look": "Прво изаберите изглед у Изгледима",
|
||||
"render.chromatic_offset": "Хроматски помак ивица",
|
||||
"render.clear_stage": "Уклони сачувану сцену",
|
||||
"render.compare_divider": "Разделник поређења",
|
||||
"render.compare_live": "Уживо",
|
||||
"render.compare_live_preview": "Преглед уживо",
|
||||
"render.compare_render": "Рендер {number}",
|
||||
"render.compare_side": "Рендер {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} s · {renderer} · забележено у ревизији {revision}",
|
||||
"render.compare_swap": "Замени стране",
|
||||
"render.copy_request": "Копирај JSON",
|
||||
"render.cpu_aa_help": "Просторни узорци: више узорака побољшава ивице и продужава рендеровање.",
|
||||
"render.cpu_area_add": "Додај правоугаоно CPU светло",
|
||||
@@ -3415,6 +3470,10 @@
|
||||
"render.result_retained": "Приказује се последња завршена слика; новије рендеровање је још није заменило.",
|
||||
"render.result_sequence": "Контролна тачка",
|
||||
"render.result_title": "Резултат рендеровања",
|
||||
"render.results_choose": "Задржани резултати",
|
||||
"render.results_label": "Рендер {number} · {shot} · {seconds} s",
|
||||
"render.results_stale_label": "{result} [застарео]",
|
||||
"render.results_unnamed": "Слика",
|
||||
"render.return_design": "Назад на Дизајн",
|
||||
"render.review_stale": "Овај преглед је застарео. Решите га или поново проверите пре примене.",
|
||||
"render.ribbon_aging": "Старење",
|
||||
@@ -3898,10 +3957,7 @@
|
||||
"the two origins are on the same component": "оба исходишта су на истој компоненти",
|
||||
"thread.automatic_size": "Аутоматска величина",
|
||||
"thread.designation": "Ознака",
|
||||
"thread.direction": "Смер навоја",
|
||||
"thread.iso_metric": "ISO метрички",
|
||||
"thread.left_hand": "Леви",
|
||||
"thread.right_hand": "Десни",
|
||||
"thread.select_size": "Изаберите величину",
|
||||
"timeline.delete_after_marker_one": "Избриши 1 операцију након ознаке враћања",
|
||||
"timeline.delete_after_marker_other": "Избриши операције након ознаке враћања: {count}",
|
||||
@@ -4075,6 +4131,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Покрените ово у терминалу, а затим притисните Повежи поново:",
|
||||
"ui.agent_chat_auth_title": "Пријави се код агента",
|
||||
"ui.agent_chat_cancel": "Откажи",
|
||||
"ui.agent_chat_cancel_timeout": "Агент није одговорио на Заустави па је веза са њим прекинута. Поново се повежите да бисте наставили.",
|
||||
"ui.agent_chat_cannot_start": "Инсталиран, али се не покреће: {error}",
|
||||
"ui.agent_chat_changes": "Промене: {n}, један корак поништавања",
|
||||
"ui.agent_chat_changes_n": "Промене: {n}",
|
||||
@@ -4084,11 +4141,13 @@
|
||||
"ui.agent_chat_command": "Команда агента",
|
||||
"ui.agent_chat_connect": "Повежи",
|
||||
"ui.agent_chat_copy": "Копирај",
|
||||
"ui.agent_chat_copy_message": "Копирај поруку",
|
||||
"ui.agent_chat_ctx_bodies": "Тела",
|
||||
"ui.agent_chat_ctx_elements": "Елементи",
|
||||
"ui.agent_chat_ctx_features": "Операције",
|
||||
"ui.agent_chat_ctx_more": "+још {n}",
|
||||
"ui.agent_chat_ctx_other": "Остало",
|
||||
"ui.agent_chat_custom_heading": "Прилагођени модел",
|
||||
"ui.agent_chat_custom_hint": "Било који програм који говори Agent Client Protocol на stdin и stdout. Не покреће га никаква љуска.",
|
||||
"ui.agent_chat_denied": "Одбијено",
|
||||
"ui.agent_chat_deny": "Одбиј",
|
||||
@@ -4132,8 +4191,16 @@
|
||||
"ui.agent_chat_mode_all": "Све",
|
||||
"ui.agent_chat_mode_ask": "Питај",
|
||||
"ui.agent_chat_mode_edits": "Измене",
|
||||
"ui.agent_chat_model_custom": "Користи прилагођени: {model}",
|
||||
"ui.agent_chat_model_no_tools": "{model} не може да користи SolidCraft алате, па може само да разговара. Изаберите модел са подршком за алате.",
|
||||
"ui.agent_chat_model_rejected": "Агент није прихватио модел {model}: {error}",
|
||||
"ui.agent_chat_model_search": "Претражи моделе…",
|
||||
"ui.agent_chat_model_tip": "Модел који агент користи. Ваш избор се памти за овог агента.",
|
||||
"ui.agent_chat_models_failed": "Списак модела није могуће учитати: {error}",
|
||||
"ui.agent_chat_models_heading": "Доступни модели",
|
||||
"ui.agent_chat_models_listing": "Учитавање списка модела…",
|
||||
"ui.agent_chat_models_none": "Крајња тачка не наводи ниједан модел",
|
||||
"ui.agent_chat_models_not_a_list": "Одговор није списак модела",
|
||||
"ui.agent_chat_new": "Нови разговор",
|
||||
"ui.agent_chat_new_confirm": "Започети нови разговор? Тренутни ће бити обрисан.",
|
||||
"ui.agent_chat_next_picture": "Следећа слика",
|
||||
@@ -4407,9 +4474,11 @@
|
||||
"ui.construction.contact_hint": "Тангента: изаберите круг/лук или отворени сплајн/конику, па тачку додира (криве слободног облика: крајње тачке)",
|
||||
"ui.construction.external_hint": "Тангента: изаберите круг или лук, спољашњу тачку, па жељено решење",
|
||||
"ui.construction.free_hint": "Помоћна права: кликните две тачке; десним кликом почните испочетка",
|
||||
"ui.construction.normal_needs_length": "Бесконачна линија је увек помоћна: упишите дужину да бисте нацртали обичну линију",
|
||||
"ui.construction.point_hint": "Помоћна права: кликните тачку кроз коју пролази",
|
||||
"ui.construction.reference_hint": "Помоћна права: изаберите праву линију, па тачку; десним кликом промените референцу",
|
||||
"ui.construction.ribbon_label": "Пом. праве",
|
||||
"ui.construction.side_hint": "Помоћна права: кликните страну ка којој линија иде; десним кликом почните испочетка",
|
||||
"ui.context_menu": "Контекстни мени",
|
||||
"ui.continuity_g0": "G0 прегиб",
|
||||
"ui.continuity_g1": "G1 тангентно",
|
||||
@@ -4675,6 +4744,7 @@
|
||||
"ui.hideOrigin": "Сакриј исходиште",
|
||||
"ui.hidePlanes": "Сакриј равни",
|
||||
"ui.hideProfile": "Сакриј профил",
|
||||
"ui.hide_component_history": "Сакриј њену временску траку",
|
||||
"ui.hide_origin_by_default": "Подразумевано сакриј исходиште",
|
||||
"ui.hide_origin_note": "Нови дизајни почињу са скривеном фасциклом Исходиште. Дизајн задржава сопствено подешавање Исходишта када га прикажете или сакријете.",
|
||||
"ui.history_back": "Иди корак назад",
|
||||
@@ -4682,6 +4752,7 @@
|
||||
"ui.history_edge_hint": "двапут кликните ивицу",
|
||||
"ui.history_first": "Иди на почетак",
|
||||
"ui.history_forward": "Иди корак напред",
|
||||
"ui.history_hide_hidden": "Сакриј скривене компоненте",
|
||||
"ui.history_hide_names": "Сакриј називе",
|
||||
"ui.history_last": "Иди на крај",
|
||||
"ui.history_marker_tip": "Ознака враћања: превуците да вратите модел уназад; нове операције долазе овде",
|
||||
@@ -4691,10 +4762,12 @@
|
||||
"ui.history_play_button_show": "Прикажи дугме за репродукцију",
|
||||
"ui.history_play_finish_action": "Завршите тренутну радњу пре репродукције временске траке",
|
||||
"ui.history_play_roll_back": "Померите маркер временске траке уназад пре репродукције",
|
||||
"ui.history_show_hidden": "Прикажи скривене компоненте",
|
||||
"ui.history_show_names": "Прикажи називе",
|
||||
"ui.history_sketch_listed_under": "Наведено испод {feature}",
|
||||
"ui.history_title": "Временска трака",
|
||||
"ui.history_toggle_names": "Прикажи или сакриј називе операција",
|
||||
"ui.history_waits_for_editor": "Поништи, Понови и временска трака чекају док се не отвори уређивач операције",
|
||||
"ui.hole": "Рупа",
|
||||
"ui.hole_tap_type": "Врста навоја рупе",
|
||||
"ui.hole_type": "Врста рупе",
|
||||
@@ -4975,6 +5048,7 @@
|
||||
"ui.showDimension": "Прикажи коту",
|
||||
"ui.showOrigin": "Прикажи исходиште",
|
||||
"ui.showPlanes": "Прикажи равни",
|
||||
"ui.show_component_history": "Прикажи њену временску траку",
|
||||
"ui.show_constraints": "Ограничења",
|
||||
"ui.show_construction_geometries": "Помоћна геометрија",
|
||||
"ui.show_dimensions": "Коте",
|
||||
@@ -5201,6 +5275,7 @@
|
||||
"web.open_link": "Отворити повезани дизајн?",
|
||||
"web.open_link_confirm": "Постоје несачуване промене. Отворити повезани дизајн у новој картици?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ изабери · Enter / Tab доврши · Esc одбаци",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ избор · Enter примењује ову вредност",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ изабери · Enter примењује оно што сте уписали · Esc одбаци",
|
||||
"⌘Enter continues on GitHub.": "⌘Enter наставља на GitHub-у."
|
||||
}
|
||||
|
||||
+85
-4
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "Rektangulär",
|
||||
"Rectangular Tube": "Rektangulärt rör",
|
||||
"Reference face": "Referensyta",
|
||||
"Reference plane": "Referensplan",
|
||||
"Relief Depth": "Avlastningsdjup",
|
||||
"Relief Width": "Avlastningsbredd",
|
||||
"Remove Tools": "Ta bort verktyg",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "Sektion",
|
||||
"Section (optional)": "Sektion (valfritt)",
|
||||
"Section position": "Profilens läge",
|
||||
"Sections": "Tvärsnitt",
|
||||
"Sections (one sketch each)": "Sektioner (en skiss per sektion)",
|
||||
"Select Other": "Markera annan",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Markera annan: håll vänsterknappen stilla på modellen (eller högerklicka › Markera annan…) för att lista allt under markören; klicka igen på samma plats för att markera nästa objekt där. Dubbelklicka med mittenknappen för att gå till Hem (Inställningar › Navigering).",
|
||||
@@ -859,6 +861,7 @@
|
||||
"cmd.component_extract": "Extrahera till fil",
|
||||
"cmd.component_finish_edit": "Slutför redigering",
|
||||
"cmd.component_from_bodies": "Skapa komponenter från kroppar",
|
||||
"cmd.component_hide_history": "Dölj komponenthistorik",
|
||||
"cmd.component_insert_design": "Infoga konstruktion",
|
||||
"cmd.component_links": "Lista länkar",
|
||||
"cmd.component_list": "Lista komponenter",
|
||||
@@ -875,6 +878,7 @@
|
||||
"cmd.component_rename": "Byt namn på komponent",
|
||||
"cmd.component_replace_with_link": "Ersätt med länk",
|
||||
"cmd.component_revert_position": "Återställ läge",
|
||||
"cmd.component_show_history": "Visa komponenthistorik",
|
||||
"cmd.component_unlock": "Lås upp innehåll",
|
||||
"cmd.component_update_links": "Uppdatera länkar",
|
||||
"cmd.config_activate": "Aktivera konfiguration",
|
||||
@@ -885,6 +889,7 @@
|
||||
"cmd.config_rule": "Konfigurationsregel",
|
||||
"cmd.config_rules": "Konfigurationsregler",
|
||||
"cmd.config_table": "Visa konfigurationstabell",
|
||||
"cmd.construct.plane.axis_point": "Plan genom axel och punkt",
|
||||
"cmd.construct_axis_edge": "Axel genom kant",
|
||||
"cmd.construct_axis_normal_to_face": "Axel vinkelrät mot yta",
|
||||
"cmd.construct_axis_through_cylinder": "Axel genom cylinder/kon/torus",
|
||||
@@ -933,6 +938,11 @@
|
||||
"cmd.explode_show": "Visa sprängvy",
|
||||
"cmd.expr_evaluate": "Utvärdera uttryck",
|
||||
"cmd.face_delete": "Ta bort yta",
|
||||
"cmd.file.export_3mf": "Exportera 3MF",
|
||||
"cmd.file.export_obj": "Exportera OBJ",
|
||||
"cmd.file.export_step": "Exportera STEP",
|
||||
"cmd.file.export_stl": "Exportera STL",
|
||||
"cmd.file.export_urdf": "Exportera URDF",
|
||||
"cmd.file_export": "Exportera",
|
||||
"cmd.file_insert_mesh": "Infoga mesh",
|
||||
"cmd.file_insert_step": "Infoga STEP",
|
||||
@@ -950,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "Aktivera komponentfärger",
|
||||
"cmd.inspect_colored_components_disable": "Inaktivera komponentfärger",
|
||||
"cmd.inspect_compare": "Jämför kroppar",
|
||||
"cmd.inspect_compare_picture": "Jämför bild",
|
||||
"cmd.inspect_continuity": "Kontinuitetsanalys",
|
||||
"cmd.inspect_curvature_comb": "Krökningskamsanalys",
|
||||
"cmd.inspect_curvature_map": "Krökningskartanalys",
|
||||
@@ -1040,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "Redigera punktmoln",
|
||||
"cmd.pointcloud_insert": "Infoga punktmoln",
|
||||
"cmd.pointcloud_list": "Punktmoln",
|
||||
"cmd.render.compare.divider": "Flytta avdelaren i renderingsjämförelsen",
|
||||
"cmd.render.result.discard": "Förkasta renderingsresultat",
|
||||
"cmd.render.result.select": "Markera renderingsresultat",
|
||||
"cmd.render_aging_describe": "Skiktad ytåldring",
|
||||
"cmd.render_backends": "Renderingsbackends",
|
||||
"cmd.render_batch": "Rendera alla tagningar",
|
||||
@@ -1254,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "Flytta tidslinjemarkör",
|
||||
"cmd.timeline_stop": "Stoppa historikuppspelning",
|
||||
"cmd.timeline_suppress": "Undertryck funktion",
|
||||
"cmd.ui.assistant": "Visa/dölj assistent",
|
||||
"cmd.view.render_browser": "Renderingswebbläsare",
|
||||
"cmd.view.render_dock": "Flytta renderingspanel",
|
||||
"cmd.view.render_focus": "Fokusvy för rendering",
|
||||
@@ -1288,9 +1303,11 @@
|
||||
"cmd.view_tool": "Navigeringsverktyg",
|
||||
"cmd.view_top": "Vy ovanifrån",
|
||||
"cmd.view_visual_style": "Visuell stil",
|
||||
"color.blue": "Blå",
|
||||
"color.copy": "Klicka för att kopiera färgvärden",
|
||||
"color.edit": "Klicka för att redigera färg",
|
||||
"color.gamma": "Visar färgvärden i gammarymd 0–255",
|
||||
"color.green": "Grön",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "Ange #rrggbb eller #rrggbbaa; sex siffror behåller opaciteten",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1298,6 +1315,7 @@
|
||||
"color.linear": "Visar färgvärden i linjär rymd 0–1",
|
||||
"color.recent": "Senaste färger",
|
||||
"color.recent_help": "Färger som du väljer i ett färgfält visas här",
|
||||
"color.red": "Röd",
|
||||
"color.saturation": "Mättnad (procent)",
|
||||
"color.saturation_value": "Mättnad och värde",
|
||||
"color.selected": "Vald färg",
|
||||
@@ -1305,6 +1323,8 @@
|
||||
"color.step_up": "Ett steg upp",
|
||||
"color.value": "Värde, ljusstyrkan (procent)",
|
||||
"config.column_filter_empty": "Inga matchande kolumner",
|
||||
"config.rule_parameter_columns": "Endast parametrar med en kolumn i tabellen listas. Lägg till en kolumn med Lägg till kolumn (+) för att använda parametern här.",
|
||||
"config.rule_suppression_columns": "Endast funktioner med en kolumn Undertryck i tabellen listas. Lägg till en kolumn med Lägg till kolumn (+) för att styra undertryckningen här.",
|
||||
"contact.components_count": "{count} komponenter",
|
||||
"contact.enabled_label": "aktiverad",
|
||||
"coupling.gear_1": "Kugghjul 1",
|
||||
@@ -1353,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "Välj kroppar eller funktioner: en skiss är ingetdera. Upprepa dess kurvor med skissens egna Mönster eller Spegla, eller extrudera den först.",
|
||||
"dialog.select": "↖ Markera",
|
||||
"dialog.selected_count": "↖ {count} markerade",
|
||||
"dialog.selection_constraint": "Villkor {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · kurva {id}",
|
||||
"dialog.selection_guidance": "Markera en indata för att ändra den. Klicka på markerade objekt för att ta bort dem; Ctrl/Cmd-klick växlar dem också. Granska förhandsvisningen och tryck sedan på OK.",
|
||||
"dialog.selection_midpoint": "Mittpunkt · {id}",
|
||||
"dialog.selection_profile": "{sketch} · profil {number}",
|
||||
"dialog.selection_step": "{step} av {total}: Markera {input}",
|
||||
"dialog.step": "Steg",
|
||||
"document.changed_on_disk": "{name} har ändrats på disken",
|
||||
@@ -1436,6 +1460,7 @@
|
||||
"help.construct.axis.two_points": "Skapar en konstruktionsaxel genom två punkter, hörn eller skisspunkter. Använd den för en axel som löper mellan två kända lägen.",
|
||||
"help.construct.plane.along_path": "Skapar ett konstruktionsplan vinkelrätt mot en kant eller kurva, på en position längs den. Använd det för att skissa en profil för en svepning eller ett tvärsnitt av en bana.",
|
||||
"help.construct.plane.at_angle": "Skapar ett konstruktionsplan som är vridet i en vinkel från ett plan eller en yta, runt en axel eller kant. Använd det för lutande skisser, till exempel ett vinklat fäste.",
|
||||
"help.construct.plane.axis_point": "Skapar ett konstruktionsplan som innehåller en axel och en punkt utanför den axeln. Välj en origoaxel eller konstruktionsaxel, en rak kant eller skisslinje och sedan ett hörn, en skisspunkt eller en konstruktionspunkt. En punkt på axeln kan inte definiera ett entydigt plan.",
|
||||
"help.construct.plane.midplane": "Skapar ett konstruktionsplan mitt emellan två plan eller plana ytor, eller som delar vinkeln där de möts i en vinkel. Använd det som symmetriplan vid spegling.",
|
||||
"help.construct.plane.offset": "Skapar ett konstruktionsplan parallellt med ett plan eller en plan yta, på ett avstånd eller genom en punkt. Använd det för att skissa på en bestämd höjd över en yta.",
|
||||
"help.construct.plane.perpendicular": "Skapar ett konstruktionsplan som innehåller en linje eller kant och står vinkelrätt mot ett plan eller en yta. Använd det för att skissa upprätt på en modell längs en kant.",
|
||||
@@ -1812,6 +1837,8 @@
|
||||
"joint_move.blocked_hint": "Kontrollera ledtypen (till exempel Styv), fixering, ledgränser och parameterstyrda värden.",
|
||||
"joint_move.blocked_title": "Det går inte att flytta den här delen",
|
||||
"joint_move.coarse": "Rotationssteg nära axeln (°)",
|
||||
"joint_move.conflict": "En led på den här delen står i konflikt med de andra: {joint}.",
|
||||
"joint_move.conflict_hint": "Redigera, undertryck eller ta bort den konflikterande leden så att de andra lederna kan placera delen.",
|
||||
"joint_move.distance": "Avstånd från start",
|
||||
"joint_move.done": "Klar",
|
||||
"joint_move.fine": "Rotationssteg långt från axeln (°)",
|
||||
@@ -1911,6 +1938,8 @@
|
||||
"motion.track_name": "Spår {number}",
|
||||
"nav.alias.help": "Shift+Alt+vänsterdrag orbiterar, Shift+Alt+mittdrag panorerar, Shift+Alt+högerdrag zoomar; mittdrag panorerar också och högerdrag orbiterar",
|
||||
"nav.blender.help": "Mittdrag orbiterar, Shift+mittdrag panorerar, Ctrl+mittdrag zoomar; högerdrag påverkar inte kameran",
|
||||
"nav.creo.help": "Mittdrag orbiterar, Shift+mittdrag panorerar, Ctrl+mittdrag i lodrät led zoomar (neråt för att zooma in); rullning med hjulet zoomar",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "Mus och 3D-mus roterar genom båda polerna och bevarar rollvinkeln. Axelsnappning med Shift är inaktiverad.",
|
||||
"nav.free_orbit.label": "Fri orbit",
|
||||
"nav.free_orbit.snap_help": "Mus och 3D-mus roterar genom båda polerna och bevarar rollvinkeln. Håll ned Shift under ett orbitdrag för att snappa vyriktningen och rollvinkeln.",
|
||||
@@ -2063,12 +2092,19 @@
|
||||
"parts.family.button_head_cap_screw": "Kullrig insexskruv",
|
||||
"parts.family.dowel_pin": "Cylindriskt stift (passtift)",
|
||||
"parts.family.hex_bolt": "Sexkantsbult / sexkantsskruv",
|
||||
"parts.family.hex_cap_screw_inch": "Sexkantsskruv (tum)",
|
||||
"parts.family.hex_nut": "Sexkantsmutter",
|
||||
"parts.family.hex_nut_inch": "Sexkantsmutter (tum)",
|
||||
"parts.family.set_screw": "Stoppskruv med insex, plan ände",
|
||||
"parts.family.socket_head_cap_screw": "Cylinderskruv med insex",
|
||||
"parts.family.washer": "Plan bricka",
|
||||
"parts.family.washer_sae": "Plan bricka, SAE (smal)",
|
||||
"parts.family.washer_uss": "Plan bricka, USS (bred)",
|
||||
"parts.holes": "Hål (valfritt)",
|
||||
"parts.placement_hint": "välj ett eller flera hål för att sätta en del i varje, eller OK placerar den i origo",
|
||||
"parts.system": "Enhetssystem",
|
||||
"parts.system.inch": "Tum",
|
||||
"parts.system.metric": "Metriskt",
|
||||
"pattern.alignment_2": "Justering 2",
|
||||
"pattern.alignment_hint": "Antalet inkluderar originalet. Centrera balanserar kopiorna; Vänd väljer den extra sidan för jämna totaler.",
|
||||
"pattern.direction_1": "Riktning 1",
|
||||
@@ -2130,6 +2166,8 @@
|
||||
"prefs.button_left": "Dra med vänsterknapp",
|
||||
"prefs.button_middle": "Dra med mittknapp",
|
||||
"prefs.button_right": "Dra med högerknapp",
|
||||
"prefs.carousel_next": "Nästa",
|
||||
"prefs.carousel_previous": "Föregående",
|
||||
"prefs.choose_font": "Välj teckensnittsfil…",
|
||||
"prefs.click": "Klick",
|
||||
"prefs.contact_shading": "Mjuk kontaktskuggning",
|
||||
@@ -2142,6 +2180,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Krökta ytor",
|
||||
"prefs.curves": "Krökta ytor",
|
||||
"prefs.cut_preview_tint": "Visa borttaget material i rött",
|
||||
"prefs.cut_preview_tint_help": "En förhandsvisning fyller det material som en funktion tar bort, och den form den ersätter, med ett rött genomskinligt spöke. När det är av är fyllningarna helt genomskinliga och bara deras konturer ritas.",
|
||||
"prefs.dark_parts_shading": "Behåll formen på mörka delar",
|
||||
"prefs.dark_parts_shading_help": "Skuggar vyn i linjärt ljus med en mjuk himmelsfyllnad, så att mörka ytor behåller en gradient i stället för att bli svarta",
|
||||
"prefs.dead_zone_note": "Det skrafferade bandet är dödzonen: rörelse inom det ignoreras.",
|
||||
@@ -2200,6 +2240,8 @@
|
||||
"prefs.interface_language": "Gränssnittsspråk",
|
||||
"prefs.interface_note": "Hur SolidCraft ser ut och var dess delar placeras.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_curved_roll_arrows": "Invertera de böjda rullningspilarna",
|
||||
"prefs.invert_curved_roll_arrows_note": "De två böjda pilarna vid kubens hörn rullar vyn i skärmplanet. På: medurspilen rullar moturs och moturspilen rullar medurs. Av: varje pil rullar vyn åt det håll den kröker sig.",
|
||||
"prefs.invert_navigator_arrows": "Invertera rotationspilarna på vykuben",
|
||||
"prefs.invert_navigator_arrows_note": "Pilarna runt kuben orbiterar kameran åt det håll de pekar, som i Fusion och Inventor: pilen till vänster pekar åt höger och svänger kameran åt höger. Av: en pil roterar kuben mot den sida den sitter på, så den vänstra pilen svänger kameran åt vänster. De böjda rullningspilarna påverkas inte.",
|
||||
"prefs.keep_work_on_tab_switch": "Behåll dialoger och verktyg öppna när du byter konstruktion",
|
||||
@@ -2259,6 +2301,8 @@
|
||||
"prefs.placement": "Placering",
|
||||
"prefs.prefer_free_sketch_points": "Föredra flyttbara skisspunkter när punkter överlappar",
|
||||
"prefs.preview_choices": "Förhandsvisning av dina val",
|
||||
"prefs.preview_tint_new_faces": "Färglägg ny yta i en förhandsvisning",
|
||||
"prefs.preview_tint_new_faces_help": "En förhandsvisning av en funktion som ändrar en kropp kan rita den yta den lägger till i förhandsvisningsfärgen. När det är av ritas kroppen som sig själv.",
|
||||
"prefs.print_face_highlight": "Markering av utskriftsyta",
|
||||
"prefs.print_face_highlight_tip": "Hur starkt en kropps utskriftsyta färgas och konturmarkeras i vyn. 0 % visar ingen markering.",
|
||||
"prefs.profile_active": "Aktiv profil",
|
||||
@@ -2556,6 +2600,10 @@
|
||||
"render.base_color": "Basfärg · sRGB",
|
||||
"render.base_metallic": "Metallandel",
|
||||
"render.base_opaque_only": "Den här redigeraren bevarar semantiken för ogenomskinliga kroppar. Använd de särskilda glasreglagen för optisk transmission.",
|
||||
"render.batch_all_shots": "Alla tagningar ({count})",
|
||||
"render.batch_empty": "Spara en tagning innan alla tagningar renderas.",
|
||||
"render.batch_limit": "Rendera alla tagningar är begränsat till 32 tagningar.",
|
||||
"render.batch_status": "{running} pågår · {waiting} väntar",
|
||||
"render.bloom_enable": "Visa bloom",
|
||||
"render.bloom_help": "Kreativ glöd, bara i utdata, från positiva linjära högdagrar och utspridd med tre normaliserade boxpass. Radien anges i utdatapixlar. Läggs till i linjärt visningsljus före sRGB; ingen lins- eller volymeffekt och ingen förbättring av konvergensen. Rå EXR förblir oförändrad.",
|
||||
"render.bloom_opaque": "Bloom kräver ogenomskinlig utdata. Transparent PNG avvisas i stället för att i det tysta plattas ut.",
|
||||
@@ -2666,6 +2714,13 @@
|
||||
"render.choose_look": "Välj först en stil i Utseenden",
|
||||
"render.chromatic_offset": "Kromatisk kantförskjutning",
|
||||
"render.clear_stage": "Ta bort sparad dekor",
|
||||
"render.compare_divider": "Jämförelsens avdelare",
|
||||
"render.compare_live": "Live",
|
||||
"render.compare_live_preview": "Liveförhandsvisning",
|
||||
"render.compare_render": "Rendering {number}",
|
||||
"render.compare_side": "Rendering {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} s · {renderer} · fångad vid revision {revision}",
|
||||
"render.compare_swap": "Byt plats",
|
||||
"render.copy_request": "Kopiera JSON",
|
||||
"render.cpu_aa_help": "Rumsliga prov: fler prov förbättrar kanterna och ökar renderingstiden.",
|
||||
"render.cpu_area_add": "Lägg till rektangulärt CPU-ljus",
|
||||
@@ -3415,6 +3470,10 @@
|
||||
"render.result_retained": "Visar den senast färdiga bilden; en nyare rendering har inte ersatt den.",
|
||||
"render.result_sequence": "Kontrollpunkt",
|
||||
"render.result_title": "Renderingsresultat",
|
||||
"render.results_choose": "Behållna resultat",
|
||||
"render.results_label": "Rendering {number} · {shot} · {seconds} s",
|
||||
"render.results_stale_label": "{result} [inaktuell]",
|
||||
"render.results_unnamed": "Bild",
|
||||
"render.return_design": "Tillbaka till Konstruktion",
|
||||
"render.review_stale": "Den här granskningen är föråldrad. Åtgärda eller kontrollera igen innan du tillämpar.",
|
||||
"render.ribbon_aging": "Åldring",
|
||||
@@ -3898,10 +3957,7 @@
|
||||
"the two origins are on the same component": "de två origona ligger på samma komponent",
|
||||
"thread.automatic_size": "Automatisk storlek",
|
||||
"thread.designation": "Beteckning",
|
||||
"thread.direction": "Gängriktning",
|
||||
"thread.iso_metric": "ISO-metrisk",
|
||||
"thread.left_hand": "Vänstergängad",
|
||||
"thread.right_hand": "Högergängad",
|
||||
"thread.select_size": "Välj storlek",
|
||||
"timeline.delete_after_marker_one": "Ta bort 1 funktion efter historikmarkören",
|
||||
"timeline.delete_after_marker_other": "Ta bort {count} funktioner efter historikmarkören",
|
||||
@@ -4075,6 +4131,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Kör detta i en terminal och tryck sedan på Anslut igen:",
|
||||
"ui.agent_chat_auth_title": "Logga in hos agenten",
|
||||
"ui.agent_chat_cancel": "Avbryt",
|
||||
"ui.agent_chat_cancel_timeout": "Agenten svarade inte på Stoppa och kopplades bort. Anslut igen för att fortsätta.",
|
||||
"ui.agent_chat_cannot_start": "Installerad, men den startar inte: {error}",
|
||||
"ui.agent_chat_changes": "Ändringar: {n}, ett ångrasteg",
|
||||
"ui.agent_chat_changes_n": "{n} ändringar",
|
||||
@@ -4084,11 +4141,13 @@
|
||||
"ui.agent_chat_command": "Agentkommando",
|
||||
"ui.agent_chat_connect": "Anslut",
|
||||
"ui.agent_chat_copy": "Kopiera",
|
||||
"ui.agent_chat_copy_message": "Kopiera meddelande",
|
||||
"ui.agent_chat_ctx_bodies": "Kroppar",
|
||||
"ui.agent_chat_ctx_elements": "Element",
|
||||
"ui.agent_chat_ctx_features": "Funktioner",
|
||||
"ui.agent_chat_ctx_more": "+{n} till",
|
||||
"ui.agent_chat_ctx_other": "Övrigt",
|
||||
"ui.agent_chat_custom_heading": "Egen modell",
|
||||
"ui.agent_chat_custom_hint": "Valfritt program som talar Agent Client Protocol via stdin och stdout. Inget skal kör det.",
|
||||
"ui.agent_chat_denied": "Nekad",
|
||||
"ui.agent_chat_deny": "Neka",
|
||||
@@ -4132,8 +4191,16 @@
|
||||
"ui.agent_chat_mode_all": "Allt",
|
||||
"ui.agent_chat_mode_ask": "Fråga",
|
||||
"ui.agent_chat_mode_edits": "Ändringar",
|
||||
"ui.agent_chat_model_custom": "Använd egen modell: {model}",
|
||||
"ui.agent_chat_model_no_tools": "{model} kan inte använda SolidCrafts verktyg, så den kan bara chatta. Välj en modell med stöd för verktyg.",
|
||||
"ui.agent_chat_model_rejected": "Agenten godtog inte {model}: {error}",
|
||||
"ui.agent_chat_model_search": "Sök modeller…",
|
||||
"ui.agent_chat_model_tip": "Modellen som agenten använder. Ditt val kommer ihåg för den här agenten.",
|
||||
"ui.agent_chat_models_failed": "Det gick inte att lista modellerna: {error}",
|
||||
"ui.agent_chat_models_heading": "Tillgängliga modeller",
|
||||
"ui.agent_chat_models_listing": "Listar modeller…",
|
||||
"ui.agent_chat_models_none": "Slutpunkten listar inga modeller",
|
||||
"ui.agent_chat_models_not_a_list": "Svaret var ingen modelllista",
|
||||
"ui.agent_chat_new": "Ny chatt",
|
||||
"ui.agent_chat_new_confirm": "Starta en ny chatt? Den nuvarande rensas.",
|
||||
"ui.agent_chat_next_picture": "Nästa bild",
|
||||
@@ -4407,14 +4474,21 @@
|
||||
"ui.construction.contact_hint": "Tangent: välj en cirkel/båge eller öppen spline/konisk kurva och sedan beröringspunkten (friformskurvor: ändpunkter)",
|
||||
"ui.construction.external_hint": "Tangent: välj en cirkel eller båge, en yttre punkt och sedan önskad lösning",
|
||||
"ui.construction.free_hint": "Konstruktionslinje: klicka på två punkter; högerklicka för att börja om",
|
||||
"ui.construction.normal_needs_length": "En oändlig linje är alltid en konstruktionslinje: skriv en längd för att rita en normal linje",
|
||||
"ui.construction.point_hint": "Konstruktionslinje: klicka på punkten som linjen ska gå genom",
|
||||
"ui.construction.reference_hint": "Konstruktionslinje: välj en rak linje och sedan en punkt; högerklicka för att byta referens",
|
||||
"ui.construction.ribbon_label": "Hjälplinjer",
|
||||
"ui.construction.side_hint": "Konstruktionslinje: klicka på den sida som linjen går mot; högerklicka för att börja om",
|
||||
"ui.context_menu": "Snabbmeny",
|
||||
"ui.continuity_crease": "Veck",
|
||||
"ui.continuity_curvature": "Krökning",
|
||||
"ui.continuity_curvature_change": "Krökningsändring",
|
||||
"ui.continuity_g0": "G0-veck",
|
||||
"ui.continuity_g1": "G1-tangent",
|
||||
"ui.continuity_g2": "G2-krökning",
|
||||
"ui.continuity_gap": "Spalt",
|
||||
"ui.continuity_position": "Läge",
|
||||
"ui.continuity_tangent": "Tangent",
|
||||
"ui.copy": "Kopiera",
|
||||
"ui.corner": "Hörn",
|
||||
"ui.corners": "Hörn",
|
||||
@@ -4675,6 +4749,7 @@
|
||||
"ui.hideOrigin": "Dölj origo",
|
||||
"ui.hidePlanes": "Dölj plan",
|
||||
"ui.hideProfile": "Dölj profil",
|
||||
"ui.hide_component_history": "Dölj dess historik",
|
||||
"ui.hide_origin_by_default": "Dölj origo som standard",
|
||||
"ui.hide_origin_note": "Nya konstruktioner startar med mappen Origo dold. En konstruktion behåller sin egen Origo-inställning när du har visat eller dolt den.",
|
||||
"ui.history_back": "Gå ett steg bakåt",
|
||||
@@ -4682,6 +4757,7 @@
|
||||
"ui.history_edge_hint": "dubbelklicka på kanten",
|
||||
"ui.history_first": "Gå till början",
|
||||
"ui.history_forward": "Gå ett steg framåt",
|
||||
"ui.history_hide_hidden": "Dölj dolda komponenter",
|
||||
"ui.history_hide_names": "Dölj namn",
|
||||
"ui.history_last": "Gå till slutet",
|
||||
"ui.history_marker_tip": "Historikmarkör: dra för att rulla tillbaka modellen; nya funktioner hamnar här",
|
||||
@@ -4691,10 +4767,12 @@
|
||||
"ui.history_play_button_show": "Visa uppspelningsknappen",
|
||||
"ui.history_play_finish_action": "Slutför den pågående åtgärden innan du spelar upp historiken",
|
||||
"ui.history_play_roll_back": "Flytta historikmarkören bakåt innan uppspelning",
|
||||
"ui.history_show_hidden": "Visa dolda komponenter",
|
||||
"ui.history_show_names": "Visa namn",
|
||||
"ui.history_sketch_listed_under": "Listad under {feature}",
|
||||
"ui.history_title": "Historik",
|
||||
"ui.history_toggle_names": "Visa eller dölj funktionsnamn",
|
||||
"ui.history_waits_for_editor": "Ångra, Gör om och Historik väntar tills funktionsredigeraren har öppnats",
|
||||
"ui.hole": "Hål",
|
||||
"ui.hole_tap_type": "Typ av gängning för hål",
|
||||
"ui.hole_type": "Hålets typ",
|
||||
@@ -4710,6 +4788,7 @@
|
||||
"ui.insert_design.saved_file": "{name} har osparade ändringar, så dess sparade fil infogades. Spara den och kör sedan Uppdatera länkar för att hämta in ändringarna.",
|
||||
"ui.insert_design.unsaved": "Osparade ändringar",
|
||||
"ui.inspect": "Granska",
|
||||
"ui.instances": "Instanser",
|
||||
"ui.intersect": "Skärning",
|
||||
"ui.isolate": "Isolera",
|
||||
"ui.join": "Sammanfoga",
|
||||
@@ -4974,6 +5053,7 @@
|
||||
"ui.showDimension": "Visa mått",
|
||||
"ui.showOrigin": "Visa origo",
|
||||
"ui.showPlanes": "Visa plan",
|
||||
"ui.show_component_history": "Visa dess historik",
|
||||
"ui.show_constraints": "Villkor",
|
||||
"ui.show_construction_geometries": "Konstruktionsgeometrier",
|
||||
"ui.show_dimensions": "Mått",
|
||||
@@ -5156,7 +5236,7 @@
|
||||
"update.check_unavailable": "Sökning efter uppdateringar är inte tillgänglig för den här installationen.",
|
||||
"update.checking": "Söker…",
|
||||
"update.copy": "Kopiera",
|
||||
"update.dmg_opened": "Diskavbilden är öppen och verifierad: dra SolidCraft till Program och ersätt den gamla kopian, starta sedan om SolidCraft.",
|
||||
"update.dmg_opened": "Diskavbilden är öppen och verifierad: avsluta SolidCraft, dra det nya SolidCraft till Program och ersätt den gamla kopian, öppna sedan SolidCraft igen.",
|
||||
"update.download": "Ladda ned",
|
||||
"update.download_open": "Ladda ned och öppna",
|
||||
"update.downloading": "Laddar ned och verifierar… {done} av {total} MB",
|
||||
@@ -5200,6 +5280,7 @@
|
||||
"web.open_link": "Öppna länkad konstruktion?",
|
||||
"web.open_link_confirm": "Det finns osparade ändringar. Öppna den länkade konstruktionen i en ny flik?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ välj · Enter / Tab komplettera · Esc stäng",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ välj · Enter tillämpar det här värdet",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ välj · Enter tillämpar det du skrev · Esc stäng",
|
||||
"⌘Enter continues on GitHub.": "⌘Enter fortsätter på GitHub."
|
||||
}
|
||||
|
||||
+85
-4
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "Dikdörtgen",
|
||||
"Rectangular Tube": "Dikdörtgen boru",
|
||||
"Reference face": "Referans yüzü",
|
||||
"Reference plane": "Referans düzlem",
|
||||
"Relief Depth": "Boşaltma derinliği",
|
||||
"Relief Width": "Boşaltma genişliği",
|
||||
"Remove Tools": "Araçları kaldır",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "Kesit",
|
||||
"Section (optional)": "Kesit (isteğe bağlı)",
|
||||
"Section position": "Kesit konumu",
|
||||
"Sections": "Kesitler",
|
||||
"Sections (one sketch each)": "Kesitler (her biri bir eskiz)",
|
||||
"Select Other": "Başka Seç",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Diğerini seç: İmlecin altındaki her şeyi listelemek için sol tuşu model üzerinde hareket ettirmeden basılı tutun (veya sağ tık › Diğerini seç…); oradaki bir sonraki öğeyi seçmek için aynı noktaya tekrar tıklayın. Başlangıç görünümüne dönmek için orta tuşla çift tıklayın (Ayarlar › Gezinme).",
|
||||
@@ -859,6 +861,7 @@
|
||||
"cmd.component_extract": "Dosyaya Çıkar",
|
||||
"cmd.component_finish_edit": "Düzenlemeyi bitir",
|
||||
"cmd.component_from_bodies": "Gövdelerden bileşen oluştur",
|
||||
"cmd.component_hide_history": "Bileşen geçmişini gizle",
|
||||
"cmd.component_insert_design": "Tasarım ekle",
|
||||
"cmd.component_links": "Bağları listele",
|
||||
"cmd.component_list": "Bileşenleri listele",
|
||||
@@ -875,6 +878,7 @@
|
||||
"cmd.component_rename": "Bileşeni yeniden adlandır",
|
||||
"cmd.component_replace_with_link": "Bağlantıyla Değiştir",
|
||||
"cmd.component_revert_position": "Konumu geri al",
|
||||
"cmd.component_show_history": "Bileşen geçmişini göster",
|
||||
"cmd.component_unlock": "İçeriğin kilidini aç",
|
||||
"cmd.component_update_links": "Bağları güncelle",
|
||||
"cmd.config_activate": "Konfigürasyonu etkinleştir",
|
||||
@@ -885,6 +889,7 @@
|
||||
"cmd.config_rule": "Konfigürasyon kuralı",
|
||||
"cmd.config_rules": "Konfigürasyon kuralları",
|
||||
"cmd.config_table": "Konfigürasyon tablosunu göster",
|
||||
"cmd.construct.plane.axis_point": "Eksen ve noktadan geçen düzlem",
|
||||
"cmd.construct_axis_edge": "Kenardan geçen eksen",
|
||||
"cmd.construct_axis_normal_to_face": "Yüze dik eksen",
|
||||
"cmd.construct_axis_through_cylinder": "Silindir/koni/torus içinden geçen eksen",
|
||||
@@ -933,6 +938,11 @@
|
||||
"cmd.explode_show": "Patlatılmış görünümü göster",
|
||||
"cmd.expr_evaluate": "İfadeyi değerlendir",
|
||||
"cmd.face_delete": "Yüzü sil",
|
||||
"cmd.file.export_3mf": "3MF olarak dışa aktar",
|
||||
"cmd.file.export_obj": "OBJ olarak dışa aktar",
|
||||
"cmd.file.export_step": "STEP olarak dışa aktar",
|
||||
"cmd.file.export_stl": "STL olarak dışa aktar",
|
||||
"cmd.file.export_urdf": "URDF olarak dışa aktar",
|
||||
"cmd.file_export": "Dışa aktar",
|
||||
"cmd.file_insert_mesh": "Ağ ekle",
|
||||
"cmd.file_insert_step": "STEP ekle",
|
||||
@@ -950,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "Bileşen Renklerini Etkinleştir",
|
||||
"cmd.inspect_colored_components_disable": "Bileşen Renklerini Devre Dışı Bırak",
|
||||
"cmd.inspect_compare": "Gövdeleri karşılaştır",
|
||||
"cmd.inspect_compare_picture": "Görüntüyle karşılaştır",
|
||||
"cmd.inspect_continuity": "Süreklilik kontrolü",
|
||||
"cmd.inspect_curvature_comb": "Eğrilik tarağı analizi",
|
||||
"cmd.inspect_curvature_map": "Eğrilik haritası analizi",
|
||||
@@ -1040,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "Nokta Bulutunu Düzenle",
|
||||
"cmd.pointcloud_insert": "Nokta Bulutu Ekle",
|
||||
"cmd.pointcloud_list": "Nokta Bulutları",
|
||||
"cmd.render.compare.divider": "Render Karşılaştırma Ayırıcısını Taşı",
|
||||
"cmd.render.result.discard": "Render Sonucunu At",
|
||||
"cmd.render.result.select": "Render Sonucunu Seç",
|
||||
"cmd.render_aging_describe": "Katmanlı Yüzey Yaşlanması",
|
||||
"cmd.render_backends": "Render arka uçları",
|
||||
"cmd.render_batch": "Tüm Çekimleri Render Et",
|
||||
@@ -1254,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "Zaman çizelgesi işaretçisini taşı",
|
||||
"cmd.timeline_stop": "Geçmişi Durdur",
|
||||
"cmd.timeline_suppress": "Özelliği bastır",
|
||||
"cmd.ui.assistant": "Asistanı aç/kapat",
|
||||
"cmd.view.render_browser": "Render tarayıcısı",
|
||||
"cmd.view.render_dock": "Render panelini taşı",
|
||||
"cmd.view.render_focus": "Render odak görünümü",
|
||||
@@ -1288,9 +1303,11 @@
|
||||
"cmd.view_tool": "Gezinme aracı",
|
||||
"cmd.view_top": "Üst Görünüm",
|
||||
"cmd.view_visual_style": "Görsel stil",
|
||||
"color.blue": "Mavi",
|
||||
"color.copy": "Renk değerlerini kopyalamak için tıklayın",
|
||||
"color.edit": "Rengi düzenlemek için tıklayın",
|
||||
"color.gamma": "Renk değerleri 0-255 gama uzayında gösteriliyor",
|
||||
"color.green": "Yeşil",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "#rrggbb veya #rrggbbaa girin; altı hane opaklığı korur",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1298,6 +1315,7 @@
|
||||
"color.linear": "Renk değerleri 0-1 doğrusal uzayda gösteriliyor",
|
||||
"color.recent": "Son kullanılan renkler",
|
||||
"color.recent_help": "Herhangi bir renk alanında seçtiğiniz renkler burada görünür",
|
||||
"color.red": "Kırmızı",
|
||||
"color.saturation": "Doygunluk (yüzde)",
|
||||
"color.saturation_value": "Doygunluk ve değer",
|
||||
"color.selected": "Seçili renk",
|
||||
@@ -1305,6 +1323,8 @@
|
||||
"color.step_up": "Bir adım yukarı",
|
||||
"color.value": "Değer, yani parlaklık (yüzde)",
|
||||
"config.column_filter_empty": "Eşleşen sütun yok",
|
||||
"config.rule_parameter_columns": "Yalnızca tabloda sütunu olan parametreler listelenir. Burada kullanmak için Sütun Ekle (+) ile bir sütun ekleyin.",
|
||||
"config.rule_suppression_columns": "Yalnızca tabloda Bastır sütunu olan özellikler listelenir. Burada denetlemek için Sütun Ekle (+) ile bir sütun ekleyin.",
|
||||
"contact.components_count": "{count} bileşen",
|
||||
"contact.enabled_label": "etkin",
|
||||
"coupling.gear_1": "Dişli 1",
|
||||
@@ -1353,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "Gövdeleri veya özellikleri seçin: eskiz bunlardan hiçbiri değildir. Eğrilerini eskizin kendi Dizi veya Aynala komutuyla tekrarlayın ya da önce ekstrüzyon yapın.",
|
||||
"dialog.select": "↖ Seç",
|
||||
"dialog.selected_count": "↖ {count} seçildi",
|
||||
"dialog.selection_constraint": "Kısıtlama {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · eğri {id}",
|
||||
"dialog.selection_guidance": "Değiştirmek için bir girdi seçin. Kaldırmak için seçili nesnelere tıklayın; Ctrl/Cmd+tıklama ile de seçime ekleyip çıkarabilirsiniz. Önizlemeyi kontrol edin ve ardından OK'e basın.",
|
||||
"dialog.selection_midpoint": "Orta nokta · {id}",
|
||||
"dialog.selection_profile": "{sketch} · profil {number}",
|
||||
"dialog.selection_step": "{step}/{total}: {input} seçin",
|
||||
"dialog.step": "Adım",
|
||||
"document.changed_on_disk": "{name} diskte değişti",
|
||||
@@ -1436,6 +1460,7 @@
|
||||
"help.construct.axis.two_points": "İki noktadan, köşe noktasından veya eskiz noktasından geçen bir yapı ekseni oluşturur. Bilinen iki konum arasında uzanan bir eksen için kullanın.",
|
||||
"help.construct.plane.along_path": "Bir kenara veya eğriye dik, onun boyunca bir konumda yapı düzlemi oluşturur. Süpürme için bir profili ya da bir yolun kesitini eskizlemek için kullanın.",
|
||||
"help.construct.plane.at_angle": "Bir düzlemden veya yüzden, bir eksen ya da kenar etrafında belirli bir açıyla döndürülmüş yapı düzlemi oluşturur. Açılı bir köşebent gibi eğimli eskizler için kullanın.",
|
||||
"help.construct.plane.axis_point": "Bir eksen ile bu eksenin dışında kalan bir noktayı içeren yapı düzlemi oluşturur. Bir orijini ya da yapı eksenini, bir düz kenarı veya eskiz çizgisini, ardından bir köşe noktasını, eskiz noktasını ya da yapı noktasını seçin. Eksen üzerindeki bir nokta tek bir düzlem belirleyemez.",
|
||||
"help.construct.plane.midplane": "İki düzlemin veya düz yüzün tam ortasında, bir açıyla buluşuyorlarsa onları ikiye bölen yapı düzlemi oluşturur. Aynalama için simetri düzlemi olarak kullanın.",
|
||||
"help.construct.plane.offset": "Bir düzleme veya düz yüze paralel, belirli bir mesafede ya da bir noktadan geçen yapı düzlemi oluşturur. Bir yüzün belirli bir yükseklik üstünde eskiz çizmek için kullanın.",
|
||||
"help.construct.plane.perpendicular": "Bir çizgiyi veya kenarı içeren ve bir düzleme ya da yüze dik duran yapı düzlemi oluşturur. Bir modelde kenar boyunca dik duran eskizler çizmek için kullanın.",
|
||||
@@ -1812,6 +1837,8 @@
|
||||
"joint_move.blocked_hint": "Eklem türünü (ör. Sabit), parçanın sabitlenme durumunu, eklem sınırlarını ve parametreyle belirlenen değerleri kontrol edin.",
|
||||
"joint_move.blocked_title": "Bu parça taşınamıyor",
|
||||
"joint_move.coarse": "Eksene yakın dönüş adımı (°)",
|
||||
"joint_move.conflict": "Bu parçanın bir bağlantısı diğerleriyle çakışıyor: {joint}.",
|
||||
"joint_move.conflict_hint": "Diğer bağlantılar parçayı yerleştirebilsin diye çakışan bağlantıyı düzenleyin, bastırın veya silin.",
|
||||
"joint_move.distance": "Başlangıçtan itibaren mesafe",
|
||||
"joint_move.done": "Tamam",
|
||||
"joint_move.fine": "Eksenden uzak dönüş adımı (°)",
|
||||
@@ -1911,6 +1938,8 @@
|
||||
"motion.track_name": "İz {number}",
|
||||
"nav.alias.help": "Shift+Alt+sol sürükleme döndürür, Shift+Alt+orta kaydırır, Shift+Alt+sağ yakınlaştırır; orta sürükleme ayrıca kaydırır, sağ sürükleme döndürür",
|
||||
"nav.blender.help": "Orta sürükleme döndürür, Shift+orta kaydırır, Ctrl+orta yakınlaştırır; sağ sürükleme kameraya dokunmaz",
|
||||
"nav.creo.help": "Orta sürükleme döndürür, Shift+orta kaydırır, Ctrl+orta dikey sürükleme yakınlaştırır (yakınlaştırmak için aşağı); tekerleği kaydırarak da yakınlaştırabilirsiniz",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "Fare ve 3B fare, her iki kutuptan geçerek döndürür ve yuvarlanmayı korur. Shift eksen hizalama devre dışı.",
|
||||
"nav.free_orbit.label": "Serbest döndürme",
|
||||
"nav.free_orbit.snap_help": "Fare ve 3B fare, her iki kutuptan geçerek döndürür ve yuvarlanmayı korur. Görünüm yönünü ve yuvarlanmayı hizalamak için döndürme sürüklemesi sırasında Shift tuşunu basılı tutun.",
|
||||
@@ -2063,12 +2092,19 @@
|
||||
"parts.family.button_head_cap_screw": "Mercimek başlı alyan cıvata",
|
||||
"parts.family.dowel_pin": "Silindirik pim (paralel)",
|
||||
"parts.family.hex_bolt": "Altıgen başlı cıvata / vida",
|
||||
"parts.family.hex_cap_screw_inch": "Altıgen başlı cıvata (inç)",
|
||||
"parts.family.hex_nut": "Altıgen somun",
|
||||
"parts.family.hex_nut_inch": "Altıgen somun (inç)",
|
||||
"parts.family.set_screw": "Alyan tespit vidası, düz uçlu",
|
||||
"parts.family.socket_head_cap_screw": "Alyan başlı cıvata",
|
||||
"parts.family.washer": "Düz rondela",
|
||||
"parts.family.washer_sae": "Düz rondela, SAE (dar)",
|
||||
"parts.family.washer_uss": "Düz rondela, USS (geniş)",
|
||||
"parts.holes": "Delikler (isteğe bağlı)",
|
||||
"parts.placement_hint": "her birine bir parça yerleştirmek için bir veya daha fazla delik seçin ya da OK ile orijine yerleştirin",
|
||||
"parts.system": "Birim sistemi",
|
||||
"parts.system.inch": "İnç",
|
||||
"parts.system.metric": "Metrik",
|
||||
"pattern.alignment_2": "Hizalama 2",
|
||||
"pattern.alignment_hint": "Adet, orijinali içerir. Ortala, kopyaları iki yana dağıtır; çift adetlerde yönü ters çevirme, fazladan kopyanın hangi tarafta olacağını belirler.",
|
||||
"pattern.direction_1": "Yön 1",
|
||||
@@ -2130,6 +2166,8 @@
|
||||
"prefs.button_left": "Sol sürükleme",
|
||||
"prefs.button_middle": "Orta sürükleme",
|
||||
"prefs.button_right": "Sağ sürükleme",
|
||||
"prefs.carousel_next": "Sonraki",
|
||||
"prefs.carousel_previous": "Önceki",
|
||||
"prefs.choose_font": "Yazı tipi dosyası seçin…",
|
||||
"prefs.click": "Tıklama",
|
||||
"prefs.contact_shading": "Yumuşak temas gölgelemesi",
|
||||
@@ -2142,6 +2180,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Eğri yüzeyler",
|
||||
"prefs.curves": "Eğri yüzeyler",
|
||||
"prefs.cut_preview_tint": "Kaldırılan malzemeyi kırmızı göster",
|
||||
"prefs.cut_preview_tint_help": "Önizleme, bir özelliğin kaldırdığı malzemeyi ve yerine geçtiği şekli kırmızı, yarı saydam bir hayaletle doldurur. Kapalıyken bu dolgular tamamen saydamdır ve yalnızca dış hatları çizilir.",
|
||||
"prefs.dark_parts_shading": "Koyu parçaların biçimini koru",
|
||||
"prefs.dark_parts_shading_help": "Görünümü doğrusal ışıkta, yumuşak bir gökyüzü dolgusuyla gölgele; böylece koyu yüzler siyaha dönmek yerine degradelerini korur",
|
||||
"prefs.dead_zone_note": "Taramalı bant ölü bölgedir: içindeki hareket yok sayılır.",
|
||||
@@ -2200,6 +2240,8 @@
|
||||
"prefs.interface_language": "Arayüz dili",
|
||||
"prefs.interface_note": "SolidCraft'ın nasıl göründüğü ve parçalarının nerede durduğu.",
|
||||
"prefs.interval_minutes": "{n} dk",
|
||||
"prefs.invert_curved_roll_arrows": "Eğri yuvarlanma oklarını ters çevir",
|
||||
"prefs.invert_curved_roll_arrows_note": "Küpün köşesindeki iki eğri ok, görünümü ekran düzlemi içinde yuvarlar. Açık: saat yönündeki ok saat yönünün tersine, saat yönünün tersindeki ok saat yönünde yuvarlar. Kapalı: her ok görünümü kıvrıldığı yöne yuvarlar.",
|
||||
"prefs.invert_navigator_arrows": "Görünüm küpünün döndürme oklarını ters çevir",
|
||||
"prefs.invert_navigator_arrows_note": "Küpün çevresindeki oklar kamerayı gösterdikleri yönde döndürür, tıpkı Fusion ve Inventor'daki gibi: soldaki ok sağı gösterir ve kamerayı sağa savurur. Kapalı: bir ok küpü bulunduğu tarafa doğru döndürür, yani sol ok kamerayı sola savurur. Kıvrık yuvarlanma okları bundan etkilenmez.",
|
||||
"prefs.keep_work_on_tab_switch": "Tasarımlar arasında geçerken diyalogları ve araçları açık tut",
|
||||
@@ -2259,6 +2301,8 @@
|
||||
"prefs.placement": "Yerleşim",
|
||||
"prefs.prefer_free_sketch_points": "Noktalar örtüştüğünde hareketli eskiz noktalarını tercih et",
|
||||
"prefs.preview_choices": "Seçimlerinizin önizlemesi",
|
||||
"prefs.preview_tint_new_faces": "Önizlemede yeni yüzeyi renklendir",
|
||||
"prefs.preview_tint_new_faces_help": "Bir gövdeyi değiştiren özelliğin önizlemesi, eklediği yüzeyi önizleme renginde çizebilir. Kapalıyken gövde olduğu gibi çizilir.",
|
||||
"prefs.print_face_highlight": "Baskı yüzeyi vurgusu",
|
||||
"prefs.print_face_highlight_tip": "Bir gövdenin baskı yüzeyinin görünümde ne kadar güçlü renklendirilip çerçeveleneceği. %0 vurgu göstermez.",
|
||||
"prefs.profile_active": "Etkin profil",
|
||||
@@ -2556,6 +2600,10 @@
|
||||
"render.base_color": "Temel renk · sRGB",
|
||||
"render.base_metallic": "Metalik oranı",
|
||||
"render.base_opaque_only": "Bu düzenleyici opak gövde anlamlarını korur. Optik geçiş için açık cam kontrollerini kullanın.",
|
||||
"render.batch_all_shots": "Tüm çekimler ({count})",
|
||||
"render.batch_empty": "Tüm çekimleri render etmeden önce bir çekim kaydedin.",
|
||||
"render.batch_limit": "Tüm Çekimleri Render Et en fazla 32 çekimle sınırlıdır.",
|
||||
"render.batch_status": "{running} çalışıyor · {waiting} bekliyor",
|
||||
"render.bloom_enable": "Ekran bloom'u",
|
||||
"render.bloom_help": "Üç normalize kutu geçişiyle yayılan, pozitif doğrusal vurgulardan çıkan yalnızca çıktıya özel parlama. Yarıçap çıktı pikselidir. sRGB öncesi doğrusal görüntü ışığına eklenir; mercek, hacimsel efekt veya yakınsama iyileştirmesi değildir. Ham EXR değişmez.",
|
||||
"render.bloom_opaque": "Bloom opak çıktı gerektirir. Saydam PNG reddedilir, sessizce düzleştirilmez.",
|
||||
@@ -2666,6 +2714,13 @@
|
||||
"render.choose_look": "Önce Görünümler'de bir görünüm seçin",
|
||||
"render.chromatic_offset": "Kromatik kenar kayması",
|
||||
"render.clear_stage": "Kaydedilmiş sahneyi kaldır",
|
||||
"render.compare_divider": "Karşılaştırma ayırıcısı",
|
||||
"render.compare_live": "Canlı",
|
||||
"render.compare_live_preview": "Canlı önizleme",
|
||||
"render.compare_render": "Render {number}",
|
||||
"render.compare_side": "Render {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} sn · {renderer} · {revision}. revizyonda yakalandı",
|
||||
"render.compare_swap": "Yer değiştir",
|
||||
"render.copy_request": "JSON kopyala",
|
||||
"render.cpu_aa_help": "Uzamsal örnekler: daha fazla örnek kenarları iyileştirir ve render süresini artırır.",
|
||||
"render.cpu_area_add": "Dikdörtgen CPU ışığı ekle",
|
||||
@@ -3415,6 +3470,10 @@
|
||||
"render.result_retained": "En son tamamlanan görüntü gösteriliyor; daha yeni bir render onu değiştirmedi.",
|
||||
"render.result_sequence": "Kontrol noktası",
|
||||
"render.result_title": "Render Sonucu",
|
||||
"render.results_choose": "Saklanan sonuçlar",
|
||||
"render.results_label": "Render {number} · {shot} · {seconds} sn",
|
||||
"render.results_stale_label": "{result} [güncel değil]",
|
||||
"render.results_unnamed": "Görüntü",
|
||||
"render.return_design": "Tasarıma dön",
|
||||
"render.review_stale": "Bu inceleme bayat. Uygulamadan önce çözün veya yeniden kontrol edin.",
|
||||
"render.ribbon_aging": "Yaşlanma",
|
||||
@@ -3898,10 +3957,7 @@
|
||||
"the two origins are on the same component": "iki orijin aynı bileşende",
|
||||
"thread.automatic_size": "Otomatik boyut",
|
||||
"thread.designation": "Tanım",
|
||||
"thread.direction": "Diş yönü",
|
||||
"thread.iso_metric": "ISO metrik",
|
||||
"thread.left_hand": "Sol el",
|
||||
"thread.right_hand": "Sağ el",
|
||||
"thread.select_size": "Boyut seç",
|
||||
"timeline.delete_after_marker_one": "Geçmiş İşaretçisinden Sonra 1 Özelliği Sil",
|
||||
"timeline.delete_after_marker_other": "Geçmiş İşaretçisinden Sonra {count} Özelliği Sil",
|
||||
@@ -4075,6 +4131,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Bunu bir terminalde çalıştırın ve ardından tekrar Bağlan'a tıklayın:",
|
||||
"ui.agent_chat_auth_title": "Ajan için oturum aç",
|
||||
"ui.agent_chat_cancel": "İptal",
|
||||
"ui.agent_chat_cancel_timeout": "Ajan Durdur komutuna yanıt vermedi ve bağlantısı kesildi. Devam etmek için yeniden bağlanın.",
|
||||
"ui.agent_chat_cannot_start": "Yüklü ama başlamıyor: {error}",
|
||||
"ui.agent_chat_changes": "{n} değişiklik, tek geri alma adımı",
|
||||
"ui.agent_chat_changes_n": "{n} değişiklik",
|
||||
@@ -4084,11 +4141,13 @@
|
||||
"ui.agent_chat_command": "Ajan komutu",
|
||||
"ui.agent_chat_connect": "Bağlan",
|
||||
"ui.agent_chat_copy": "Kopyala",
|
||||
"ui.agent_chat_copy_message": "İletiyi kopyala",
|
||||
"ui.agent_chat_ctx_bodies": "Gövdeler",
|
||||
"ui.agent_chat_ctx_elements": "Öğeler",
|
||||
"ui.agent_chat_ctx_features": "Özellikler",
|
||||
"ui.agent_chat_ctx_more": "+{n} daha",
|
||||
"ui.agent_chat_ctx_other": "Diğer",
|
||||
"ui.agent_chat_custom_heading": "Özel model",
|
||||
"ui.agent_chat_custom_hint": "stdin ve stdout üzerinden Agent Client Protocol'ü kullanan herhangi bir program. Kabuk (shell) olmadan çalıştırılır.",
|
||||
"ui.agent_chat_denied": "Reddedildi",
|
||||
"ui.agent_chat_deny": "Reddet",
|
||||
@@ -4132,8 +4191,16 @@
|
||||
"ui.agent_chat_mode_all": "Tümü",
|
||||
"ui.agent_chat_mode_ask": "Sor",
|
||||
"ui.agent_chat_mode_edits": "Düzenle",
|
||||
"ui.agent_chat_model_custom": "Özel olanı kullan: {model}",
|
||||
"ui.agent_chat_model_no_tools": "{model}, SolidCraft'ın araçlarını kullanamaz, bu yüzden yalnızca sohbet edebilir. Araç destekli bir model seçin.",
|
||||
"ui.agent_chat_model_rejected": "Ajan {model} modelini kabul etmedi: {error}",
|
||||
"ui.agent_chat_model_search": "Model ara…",
|
||||
"ui.agent_chat_model_tip": "Ajanın kullandığı model. Seçiminiz bu ajan için hatırlanır.",
|
||||
"ui.agent_chat_models_failed": "Modeller listelenemedi: {error}",
|
||||
"ui.agent_chat_models_heading": "Kullanılabilir modeller",
|
||||
"ui.agent_chat_models_listing": "Modeller listeleniyor…",
|
||||
"ui.agent_chat_models_none": "Uç nokta hiç model listelemiyor",
|
||||
"ui.agent_chat_models_not_a_list": "Yanıt bir model listesi değildi",
|
||||
"ui.agent_chat_new": "Yeni sohbet",
|
||||
"ui.agent_chat_new_confirm": "Yeni bir sohbet başlatılsın mı? Geçerli sohbet temizlenir.",
|
||||
"ui.agent_chat_next_picture": "Sonraki resim",
|
||||
@@ -4407,14 +4474,21 @@
|
||||
"ui.construction.contact_hint": "Teğet: bir daire/yay veya açık spline/konik eğri, ardından değme noktasını seçin (serbest biçimli eğriler: uç noktalar)",
|
||||
"ui.construction.external_hint": "Teğet: bir daire veya yay, bir dış nokta, ardından istenen çözümü seçin",
|
||||
"ui.construction.free_hint": "Yardımcı doğru: iki noktaya tıklayın; yeniden başlamak için sağ tıklayın",
|
||||
"ui.construction.normal_needs_length": "Sonsuz çizgi her zaman yardımcı doğrudur: normal bir çizgi çizmek için bir uzunluk yazın",
|
||||
"ui.construction.point_hint": "Yardımcı doğru: geçeceği noktaya tıklayın",
|
||||
"ui.construction.reference_hint": "Yardımcı doğru: düz bir çizgi, ardından bir nokta seçin; referansı değiştirmek için sağ tıklayın",
|
||||
"ui.construction.ribbon_label": "Yardımcılar",
|
||||
"ui.construction.side_hint": "Yardımcı doğru: doğrunun ilerleyeceği tarafa tıklayın; yeniden başlamak için sağ tıklayın",
|
||||
"ui.context_menu": "Bağlam menüsü",
|
||||
"ui.continuity_crease": "Kırık",
|
||||
"ui.continuity_curvature": "Eğrilik",
|
||||
"ui.continuity_curvature_change": "Eğrilik değişimi",
|
||||
"ui.continuity_g0": "G0 kırık",
|
||||
"ui.continuity_g1": "G1 teğet",
|
||||
"ui.continuity_g2": "G2 eğrilik",
|
||||
"ui.continuity_gap": "Boşluk",
|
||||
"ui.continuity_position": "Konum",
|
||||
"ui.continuity_tangent": "Teğet",
|
||||
"ui.copy": "Kopyala",
|
||||
"ui.corner": "Köşe",
|
||||
"ui.corners": "Köşeler",
|
||||
@@ -4675,6 +4749,7 @@
|
||||
"ui.hideOrigin": "Orijini gizle",
|
||||
"ui.hidePlanes": "Düzlemleri gizle",
|
||||
"ui.hideProfile": "Profili gizle",
|
||||
"ui.hide_component_history": "Geçmişini gizle",
|
||||
"ui.hide_origin_by_default": "Başlangıcı varsayılan olarak gizle",
|
||||
"ui.hide_origin_note": "Yeni tasarımlar Başlangıç klasörü gizli başlar. Bir tasarım, gösterip gizledikten sonra kendi Başlangıç ayarını korur.",
|
||||
"ui.history_back": "Bir adım geri git",
|
||||
@@ -4682,6 +4757,7 @@
|
||||
"ui.history_edge_hint": "kenara çift tıklayın",
|
||||
"ui.history_first": "Başlangıca git",
|
||||
"ui.history_forward": "Bir adım ileri git",
|
||||
"ui.history_hide_hidden": "Gizli Bileşenleri Gizle",
|
||||
"ui.history_hide_names": "Adları gizle",
|
||||
"ui.history_last": "Sona git",
|
||||
"ui.history_marker_tip": "Geçmiş işaretçisi: modeli geri sarmak için sürükleyin; yeni özellikler buraya eklenir",
|
||||
@@ -4691,10 +4767,12 @@
|
||||
"ui.history_play_button_show": "Oynat Düğmesini Göster",
|
||||
"ui.history_play_finish_action": "Geçmişi oynatmadan önce geçerli eylemi bitirin",
|
||||
"ui.history_play_roll_back": "Oynatmadan önce Geçmiş işaretçisini geri taşıyın",
|
||||
"ui.history_show_hidden": "Gizli Bileşenleri Göster",
|
||||
"ui.history_show_names": "Adları göster",
|
||||
"ui.history_sketch_listed_under": "{feature} altında listelenir",
|
||||
"ui.history_title": "Geçmiş",
|
||||
"ui.history_toggle_names": "Özellik adlarını göster veya gizle",
|
||||
"ui.history_waits_for_editor": "Geri al, Yinele ve Geçmiş, özellik düzenleyicisi açılana kadar bekler",
|
||||
"ui.hole": "Delik",
|
||||
"ui.hole_tap_type": "Diş türü",
|
||||
"ui.hole_type": "Delik türü",
|
||||
@@ -4710,6 +4788,7 @@
|
||||
"ui.insert_design.saved_file": "{name} kaydedilmemiş değişiklikler içeriyor, bu yüzden kaydedilmiş dosyası eklendi. Değişiklikleri almak için onu kaydedin, ardından Bağları güncelle'yi çalıştırın.",
|
||||
"ui.insert_design.unsaved": "Kaydedilmemiş değişiklikler",
|
||||
"ui.inspect": "İncele",
|
||||
"ui.instances": "Örnekler",
|
||||
"ui.intersect": "Kesişim",
|
||||
"ui.isolate": "Yalıt",
|
||||
"ui.join": "Birleştir",
|
||||
@@ -4974,6 +5053,7 @@
|
||||
"ui.showDimension": "Ölçüyü göster",
|
||||
"ui.showOrigin": "Orijini göster",
|
||||
"ui.showPlanes": "Düzlemleri göster",
|
||||
"ui.show_component_history": "Geçmişini göster",
|
||||
"ui.show_constraints": "Kısıtlamalar",
|
||||
"ui.show_construction_geometries": "Yapı geometrileri",
|
||||
"ui.show_dimensions": "Ölçüler",
|
||||
@@ -5156,7 +5236,7 @@
|
||||
"update.check_unavailable": "Bu kurulum için güncelleme denetimi kullanılamıyor.",
|
||||
"update.checking": "Denetleniyor…",
|
||||
"update.copy": "Kopyala",
|
||||
"update.dmg_opened": "Disk görüntüsü açıldı ve doğrulandı: SolidCraft'ı “Uygulamalar” klasörüne sürükleyin, eski sürümü değiştirin ve SolidCraft'ı yeniden başlatın.",
|
||||
"update.dmg_opened": "Disk görüntüsü açıldı ve doğrulandı: SolidCraft'tan çıkın, yeni SolidCraft'ı “Uygulamalar” klasörüne sürükleyin, eski sürümü değiştirin ve SolidCraft'ı yeniden açın.",
|
||||
"update.download": "İndir",
|
||||
"update.download_open": "İndir ve aç",
|
||||
"update.downloading": "İndiriliyor ve doğrulanıyor… {done} / {total} MB",
|
||||
@@ -5200,6 +5280,7 @@
|
||||
"web.open_link": "Bağlı tasarım açılsın mı?",
|
||||
"web.open_link_confirm": "Kaydedilmemiş değişiklikler var. Bağlı tasarım yeni bir sekmede açılsın mı?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ seç · Enter / Tab tamamla · Esc kapat",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ seç · Enter bu değeri uygular",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ seçin · Enter yazdığınızı uygular · Esc kapatır",
|
||||
"⌘Enter continues on GitHub.": "⌘Enter ile GitHub'da devam edilir."
|
||||
}
|
||||
|
||||
@@ -2121,6 +2121,8 @@
|
||||
"prefs.bottom_centre": "Внизу по центру",
|
||||
"prefs.bottom_right": "Внизу праворуч",
|
||||
"prefs.browser": "Браузер",
|
||||
"prefs.browser_components_open": "Компоненти розгорнуті з початку",
|
||||
"prefs.browser_components_open_note": "Кожен компонент у браузері одразу розгорнутий, як раніше, а не згорнутий, доки ви його не розгорнете.",
|
||||
"prefs.browser_eye_left": "Око поруч зі стрілкою згортання",
|
||||
"prefs.browser_eye_left_note": "Рядки браузера показують око «показати/приховати» поруч зі стрілкою згортання, як і раніше, а не в стовпці на правому краї рядків.",
|
||||
"prefs.browser_reveal_on_hover": "Показувати в браузері при наведенні",
|
||||
@@ -3898,10 +3900,7 @@
|
||||
"the two origins are on the same component": "обидва початки координат належать одному компоненту",
|
||||
"thread.automatic_size": "Автоматичний розмір",
|
||||
"thread.designation": "Позначення",
|
||||
"thread.direction": "Напрямок різьби",
|
||||
"thread.iso_metric": "Метрична ISO",
|
||||
"thread.left_hand": "Ліва",
|
||||
"thread.right_hand": "Права",
|
||||
"thread.select_size": "Вибрати розмір",
|
||||
"timeline.delete_after_marker_one": "Видалити 1 операцію після маркера історії",
|
||||
"timeline.delete_after_marker_other": "Видалити операції після маркера історії: {count}",
|
||||
@@ -4411,10 +4410,15 @@
|
||||
"ui.construction.reference_hint": "Допоміжна пряма: виберіть пряму, потім точку; клацніть правою кнопкою, щоб змінити опорний об'єкт",
|
||||
"ui.construction.ribbon_label": "Доп. лінії",
|
||||
"ui.context_menu": "Контекстне меню",
|
||||
"ui.continuity_crease": "Злам",
|
||||
"ui.continuity_curvature": "Кривизна",
|
||||
"ui.continuity_curvature_change": "Зміна кривини",
|
||||
"ui.continuity_g0": "G0 злам",
|
||||
"ui.continuity_g1": "G1 дотичність",
|
||||
"ui.continuity_g2": "G2 кривина",
|
||||
"ui.continuity_gap": "Проміжок",
|
||||
"ui.continuity_position": "Положення",
|
||||
"ui.continuity_tangent": "Дотична",
|
||||
"ui.copy": "Копіювати",
|
||||
"ui.corner": "Кут",
|
||||
"ui.corners": "Кути",
|
||||
@@ -5157,7 +5161,7 @@
|
||||
"update.check_unavailable": "Перевірка оновлень недоступна для цього встановлення.",
|
||||
"update.checking": "Перевірка…",
|
||||
"update.copy": "Копіювати",
|
||||
"update.dmg_opened": "Образ диска відкрито й перевірено: перетягніть SolidCraft до Applications, замінивши стару копію, а потім перезапустіть SolidCraft.",
|
||||
"update.dmg_opened": "Образ диска відкрито й перевірено: завершіть SolidCraft, перетягніть новий SolidCraft до Applications, замінивши стару копію, а потім знову відкрийте SolidCraft.",
|
||||
"update.download": "Завантажити",
|
||||
"update.download_open": "Завантажити й відкрити",
|
||||
"update.downloading": "Завантаження та перевірка… {done} з {total} МБ",
|
||||
|
||||
+188
-14
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "Hình chữ nhật",
|
||||
"Rectangular Tube": "Thép hộp chữ nhật",
|
||||
"Reference face": "Bề mặt tham chiếu",
|
||||
"Reference plane": "Mặt phẳng tham chiếu",
|
||||
"Relief Depth": "Độ sâu rãnh thoát",
|
||||
"Relief Width": "Chiều rộng rãnh thoát",
|
||||
"Remove Tools": "Loại bỏ công cụ cắt",
|
||||
@@ -494,7 +495,8 @@
|
||||
"Rename Folder": "Đổi tên thư mục",
|
||||
"Render Stage": "Giai đoạn kết xuất",
|
||||
"Reopen designs": "Mở lại thiết kế",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "Mở lại các thiết kế đã mở khi đóng SolidCraft lần trước",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "Mở lại các thiết kế đã mở lần trước",
|
||||
"Repair Part": "Sửa chi tiết",
|
||||
"Replace existing library file": "Thay thế tệp thư viện hiện có",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "Báo cáo sự cố — xem lại ảnh chụp màn hình và mở bản nháp trên GitHub",
|
||||
"Report copied. Keep this text to submit on GitHub when you are online.": "Đã sao chép báo cáo. Hãy giữ lại nội dung này để gửi lên GitHub khi có kết nối mạng.",
|
||||
@@ -789,6 +791,7 @@
|
||||
"browser.edit_canvas_title": "Sửa ảnh nền Canvas: {name}",
|
||||
"browser.edit_point_cloud_title": "Chỉnh sửa đám mây điểm: {name}",
|
||||
"browser.move_title": "Di chuyển: {name}",
|
||||
"browser.needs_repair": "{name}: cần sửa",
|
||||
"browser.parameters": "Bảng tham số ({count})",
|
||||
"browser.precision": "Độ chính xác: {precision}",
|
||||
"browser.sketch_attachment_lost": "{name} (mất liên kết)",
|
||||
@@ -833,6 +836,7 @@
|
||||
"cmd.assembly_mass_properties": "Thuộc tính khối lượng",
|
||||
"cmd.body.print_face": "Mặt in",
|
||||
"cmd.body_rename": "Đổi tên thân khối",
|
||||
"cmd.body_repair": "Sửa chi tiết",
|
||||
"cmd.browser_collapse_all": "Thu gọn tất cả",
|
||||
"cmd.browser_expand_all": "Mở rộng tất cả",
|
||||
"cmd.browser_group": "Tạo nhóm mới",
|
||||
@@ -917,6 +921,8 @@
|
||||
"cmd.diagnostics_crash_reports": "Danh sách báo cáo sự cố",
|
||||
"cmd.diagnostics_crash_reports_clear": "Xóa báo cáo sự cố ứng dụng",
|
||||
"cmd.diagnostics_crash_test": "Kiểm tra cơ chế sập ứng dụng",
|
||||
"cmd.diagnostics_profile": "Hồ sơ đo",
|
||||
"cmd.diagnostics_validation": "Kiểm tra",
|
||||
"cmd.doc_recover": "Khôi phục thiết kế",
|
||||
"cmd.doc_recovery_discard": "Hủy thiết kế khôi phục",
|
||||
"cmd.doc_recovery_list": "Danh sách thiết kế có thể khôi phục",
|
||||
@@ -954,6 +960,7 @@
|
||||
"cmd.frame_profile_list": "Thư viện tiết diện",
|
||||
"cmd.frame_profile_properties": "Thuộc tính tiết diện",
|
||||
"cmd.inspect_accessibility": "Phân tích khả năng gia công tiếp cận",
|
||||
"cmd.inspect_analysis": "Phân tích khung nhìn",
|
||||
"cmd.inspect_center_of_mass": "Trọng tâm",
|
||||
"cmd.inspect_clearance": "Khoảng cách an toàn",
|
||||
"cmd.inspect_colored_components": "Bật màu cụm chi tiết",
|
||||
@@ -977,6 +984,7 @@
|
||||
"cmd.joint_delete": "Xóa Khớp nối cơ khí",
|
||||
"cmd.joint_drive": "Dẫn động các khớp nối",
|
||||
"cmd.joint_edit": "Chỉnh sửa khớp nối",
|
||||
"cmd.joint_find_limits": "Tìm giới hạn khớp nối",
|
||||
"cmd.joint_gear": "Khớp bánh răng",
|
||||
"cmd.joint_inspect": "Kiểm tra khớp nối",
|
||||
"cmd.joint_limits": "Giới hạn chuyển động khớp nối",
|
||||
@@ -1358,6 +1366,7 @@
|
||||
"crash.unclean_explanation": "Được phát hiện ở lần khởi chạy sau; thời điểm thoát chính xác không được ghi lại. Việc tắt máy hoặc buộc dừng cũng có thể gây ra thoát bất thường.",
|
||||
"delete.and_more": "… và {count} mục khác",
|
||||
"diagnostic.empty_boolean_component": "không có thân khối nào để cắt hoặc giao trong cụm chi tiết '{component}'; công cụ có thể giao nhau với '{body}' trong cụm chi tiết '{other}' — hãy kích hoạt cụm chi tiết đó hoặc chọn thân khối làm đối tượng đích",
|
||||
"diagnostic.reselect_reference": "{feature}: tham chiếu `{reference}` không còn tồn tại như khi được chọn; hãy chọn lại",
|
||||
"dialog.bodies_count": "{count} thân khối",
|
||||
"dialog.clearance_invalid": "khoảng cách an toàn phải có chiều dài từ 0 trở lên",
|
||||
"dialog.compute": "Tính toán",
|
||||
@@ -1423,6 +1432,14 @@
|
||||
"file_load.reading_percent": "Đang đọc tệp... {percent}%",
|
||||
"file_load.rebuilding": "Đang dựng lại tính năng {done} trên {total}",
|
||||
"file_load.recovering": "Đang khôi phục thiết kế",
|
||||
"file_load.step_build": "Đang dựng mô hình",
|
||||
"file_load.step_feature": "Đặc trưng {done} trong {total}",
|
||||
"file_load.step_part": "Chi tiết {done} trong {total}",
|
||||
"file_load.step_percent": "{percent}%",
|
||||
"file_load.step_read": "Đang đọc tệp",
|
||||
"file_load.step_rebuild": "Đang dựng lại các đặc trưng",
|
||||
"file_load.step_repair": "Đang kiểm tra và sửa các chi tiết",
|
||||
"file_load.step_show": "Đang chuẩn bị khung nhìn",
|
||||
"file_load.stopping": "Đang chờ thao tác trước đó dừng lại...",
|
||||
"file_load.updating_links": "Đang cập nhật liên kết",
|
||||
"file_load.waiting": "Đang chờ thao tác tệp",
|
||||
@@ -1717,6 +1734,7 @@
|
||||
"help.timeline.delete": "Xóa các tính năng đã chọn khỏi lịch sử; xóa một bản phác thảo cũng xóa các tính năng dựng từ nó. Dùng để bỏ một bước không còn cần.",
|
||||
"help.timeline.dependents": "Liệt kê các tính năng sẽ bị xóa cùng một tính năng, và những tính năng sẽ lỗi nếu thiếu nó. Dùng trước khi xóa hoặc thay đổi một tính năng.",
|
||||
"help.timeline.play": "Phát lại lần lượt các tính năng còn lại, tạm dừng nửa giây sau mỗi lần dựng lại. Chạy lại lệnh này hoặc nhấn Escape để dừng; Đến cuối sẽ chuyển ngay đến cuối lịch sử.",
|
||||
"help.version": "Phiên bản {version}",
|
||||
"help.view.apply": "Xoay khung nhìn về hướng chuẩn như trước, trên hoặc iso, hoặc về một góc nhìn đã lưu. Dùng để xem mô hình từ một góc cố định.",
|
||||
"help.view.delete": "Xóa một góc nhìn đã lưu. Mô hình không bị thay đổi.",
|
||||
"help.view.fit": "Phóng to và dịch khung nhìn để toàn bộ mô hình, hoặc phần đang chọn, lấp đầy khung nhìn. Dùng khi mô hình đã trôi ra khỏi tầm mắt.",
|
||||
@@ -1804,6 +1822,27 @@
|
||||
"home.start_hint": "Tạo thiết kế, mở tệp gần đây hoặc khám phá mẫu.",
|
||||
"home.whats_new": "Có gì mới",
|
||||
"home.yesterday": "Hôm qua",
|
||||
"import_report.details": "Chi tiết:",
|
||||
"import_report.file_one": "Có 1 vấn đề khác với tệp.",
|
||||
"import_report.file_other": "Có {count} vấn đề khác với tệp.",
|
||||
"import_report.hint": "Xuất lại các chi tiết này từ chương trình đã tạo ra chúng có thể khắc phục được. Sao chép thông báo cảnh báo có đầy đủ chi tiết.",
|
||||
"import_report.left_out_one": "1 mặt không thể sửa và bị thiếu.",
|
||||
"import_report.left_out_other": "{count} mặt không thể sửa và bị thiếu.",
|
||||
"import_report.needs_repair_one": "1 chi tiết cần sửa trước khi có thể mô hình hóa tiếp.",
|
||||
"import_report.needs_repair_other": "{count} chi tiết cần sửa trước khi có thể mô hình hóa tiếp.",
|
||||
"import_report.not_imported_one": "1 chi tiết không thể nhập.",
|
||||
"import_report.not_imported_other": "{count} chi tiết không thể nhập.",
|
||||
"import_report.not_shown_one": "1 mặt không thể vẽ và bị ẩn.",
|
||||
"import_report.not_shown_other": "{count} mặt không thể vẽ và bị ẩn.",
|
||||
"import_report.open_one": "1 chi tiết có khe hở giữa các mặt và được nhập thành thân hở.",
|
||||
"import_report.open_other": "{count} chi tiết có khe hở giữa các mặt và được nhập thành thân hở.",
|
||||
"import_report.refused_one": "1 chi tiết không thể tạo thành khối đặc và không được nhập.",
|
||||
"import_report.refused_other": "{count} chi tiết không thể tạo thành khối đặc và không được nhập.",
|
||||
"import_report.repaired_one": "Đã sửa các lỗi nhỏ trong 1 chi tiết.",
|
||||
"import_report.repaired_other": "Đã sửa các lỗi nhỏ trong {count} chi tiết.",
|
||||
"import_report.solids": "Đã nhập {solids} trong {parts} chi tiết thành khối đặc.",
|
||||
"import_report.surfaces_one": "1 chi tiết không thể tạo thành khối đặc và được nhập thành bề mặt.",
|
||||
"import_report.surfaces_other": "{count} chi tiết không thể tạo thành khối đặc và được nhập thành bề mặt.",
|
||||
"install.button_tip": "Cài đặt SolidCraft dưới dạng ứng dụng: mở trong cửa sổ riêng và hoạt động ngoại tuyến",
|
||||
"install.dock_1": "Chọn Tệp › Thêm vào Dock.",
|
||||
"install.dock_2": "Nhấp vào Thêm.",
|
||||
@@ -1828,7 +1867,18 @@
|
||||
"issue.note_save_failed": "Không thể lưu ảnh chụp màn hình: {error}",
|
||||
"issue.note_saved": "Đã lưu ảnh chụp màn hình. Hãy đính kèm tệp PNG vào bản nháp GitHub.",
|
||||
"issue.save_screenshot": "Lưu ảnh chụp màn hình…",
|
||||
"joint.find_limits": "Tìm giới hạn",
|
||||
"joint.find_limits_failed": "Việc tìm giới hạn đã dừng do lỗi",
|
||||
"joint.find_limits_hint": "Điền giới hạn từ vị trí mà các chi tiết do khớp nối này di chuyển lần đầu chạm vào một chi tiết khác, khi xoay hoặc trượt theo cả hai hướng từ vị trí hiện tại. Thực hiện trên một bản sao: không có gì thay đổi cho đến khi bấm OK.",
|
||||
"joint.find_limits_stale": "Khớp nối hoặc thiết kế đã thay đổi kể từ khi tìm thấy các giới hạn này: hãy tìm lại",
|
||||
"joint.finding_limits": "Đang tìm giới hạn…",
|
||||
"joint.from_bodies_hint": "Mỗi thân khối được chọn sẽ được chuyển thành một cụm chi tiết mới riêng biệt và giữ nguyên vị trí hiện tại.",
|
||||
"joint.limit_contact": "{side} {value}: {a} chạm vào {b}",
|
||||
"joint.limit_limit": "{side} {value}: giới hạn của một giá trị hoặc khớp nối khác",
|
||||
"joint.limit_loop": "{side} {value}: vòng kín không thể khép lại quá điểm này",
|
||||
"joint.limit_range": "{side}: không có tiếp xúc trong phạm vi {range}, giữ nguyên",
|
||||
"joint.limit_turn": "{side} {value}: không có tiếp xúc trong một vòng quay",
|
||||
"joint.limits_overlapping": "Đã lồng vào nhau tại đây: {pairs}. Chúng chỉ dừng việc tìm kiếm nếu lồng sâu hơn.",
|
||||
"joint.one_body": "Cả hai điểm chọn đều nằm trên {body}. Vui lòng chọn điểm thứ hai trên chi tiết khác.",
|
||||
"joint.one_component": "Các chi tiết này thuộc cùng một cụm chi tiết: {bodies}. Khớp nối điều khiển một cụm chi tiết so với cụm chi tiết khác, vì vậy mỗi chi tiết cần một cụm chi tiết riêng.",
|
||||
"joint.rigid_group_one_component": "Tất cả {count} thân khối này đều nằm trong cùng một cụm chi tiết (hoặc trong chính thiết kế): hãy dùng lệnh {command} trước.",
|
||||
@@ -2091,12 +2141,19 @@
|
||||
"parts.family.button_head_cap_screw": "Vít đầu tròn lục giác chìm",
|
||||
"parts.family.dowel_pin": "Chốt trụ",
|
||||
"parts.family.hex_bolt": "Bu lông đầu lục giác",
|
||||
"parts.family.hex_cap_screw_inch": "Bu lông đầu lục giác (inch)",
|
||||
"parts.family.hex_nut": "Đai ốc lục giác",
|
||||
"parts.family.hex_nut_inch": "Đai ốc lục giác (inch)",
|
||||
"parts.family.set_screw": "Vít trí lục giác chìm, đầu bằng",
|
||||
"parts.family.socket_head_cap_screw": "Bu lông lục giác chìm đầu trụ",
|
||||
"parts.family.washer": "Vòng đệm phẳng",
|
||||
"parts.family.washer_sae": "Vòng đệm phẳng, SAE (hẹp)",
|
||||
"parts.family.washer_uss": "Vòng đệm phẳng, USS (rộng)",
|
||||
"parts.holes": "Lỗ (tùy chọn)",
|
||||
"parts.placement_hint": "chọn một hoặc nhiều lỗ để lắp một chi tiết vào mỗi lỗ, hoặc nhấn OK để đặt tại gốc tọa độ",
|
||||
"parts.system": "Hệ đơn vị",
|
||||
"parts.system.inch": "Inch",
|
||||
"parts.system.metric": "Hệ mét",
|
||||
"pattern.alignment_2": "Căn chỉnh 2",
|
||||
"pattern.alignment_hint": "Số lượng bao gồm cả bản gốc. Căn giữa sẽ cân bằng các bản sao; Đổi hướng sẽ chọn phía bổ sung cho tổng số chẵn.",
|
||||
"pattern.direction_1": "Hướng 1",
|
||||
@@ -2135,6 +2192,18 @@
|
||||
"prefs.alt_frees_manipulators": "Phím Alt / Option cũng giải phóng mũi tên thao tác",
|
||||
"prefs.anti_aliasing_amd_dx12": "Khử răng cưa sử dụng mức 1× vì kiểm tra khởi động không vẽ được hình ảnh với đa lấy mẫu và đã vượt qua ở mức 1×. Bạn có thể chọn cài đặt khác ở trên; hãy khởi động lại để áp dụng nếu kiểm tra vượt qua.",
|
||||
"prefs.anti_aliasing_note": "Làm mịn các cạnh răng cưa.",
|
||||
"prefs.appearance.colour_vision_help": "Thay đổi các màu mang ý nghĩa: trạng thái phác thảo, trục, đường uốn, phân tích và biểu tượng.",
|
||||
"prefs.appearance.desktop_only": "Chỉ máy tính",
|
||||
"prefs.appearance.follow_help": "Tải lại khi công cụ khác ghi lại tệp.",
|
||||
"prefs.appearance.interface_scale": "Tỷ lệ giao diện",
|
||||
"prefs.appearance.next_themes": "Giao diện tiếp theo",
|
||||
"prefs.appearance.option_count": "{count} tùy chọn",
|
||||
"prefs.appearance.option_count_one": "{count} tùy chọn",
|
||||
"prefs.appearance.previous_themes": "Giao diện trước",
|
||||
"prefs.appearance.theme_count": "{count} giao diện",
|
||||
"prefs.appearance.theme_own": "Theo giao diện",
|
||||
"prefs.appearance.viewport_colours": "Màu khung nhìn 3D",
|
||||
"prefs.appearance.viewport_logo": "Khung nhìn và logo",
|
||||
"prefs.applies_on_restart": "Áp dụng sau khi khởi động lại",
|
||||
"prefs.assistant": "Trợ lý",
|
||||
"prefs.auto_operation": "Tự động chọn thao tác",
|
||||
@@ -2149,6 +2218,8 @@
|
||||
"prefs.bottom_centre": "Giữa phía dưới",
|
||||
"prefs.bottom_right": "Góc dưới bên phải",
|
||||
"prefs.browser": "Cây thư mục",
|
||||
"prefs.browser_components_open": "Thành phần mở sẵn",
|
||||
"prefs.browser_components_open_note": "Mọi thành phần trong trình duyệt đều mở sẵn như trước, thay vì đóng cho đến khi bạn mở.",
|
||||
"prefs.browser_eye_left": "Biểu tượng mắt cạnh mũi tên thu gọn",
|
||||
"prefs.browser_eye_left_note": "Các hàng trong cây thư mục hiển thị biểu tượng mắt bật/tắt cạnh mũi tên thu gọn như trước đây, thay vì trong một cột ở đầu bên phải của các hàng.",
|
||||
"prefs.browser_reveal_on_hover": "Hiện khi di chuột",
|
||||
@@ -2198,7 +2269,19 @@
|
||||
"prefs.font_sample": "Kiểm tra phông chữ · Ø12.50 mm · 45.0° · 0123456789",
|
||||
"prefs.font_unreadable": "Không thể đọc tệp phông chữ đã chọn.",
|
||||
"prefs.general": "Cài đặt chung",
|
||||
"prefs.general_note": "Ngôn ngữ, lưu tệp và cập nhật.",
|
||||
"prefs.general.active": "Đang dùng",
|
||||
"prefs.general.crash_reports": "Báo cáo sự cố",
|
||||
"prefs.general.edited": "Đã sửa",
|
||||
"prefs.general.held": "Được lưu",
|
||||
"prefs.general.import": "Nhập...",
|
||||
"prefs.general.last_check": "Kiểm tra lần cuối: {time}",
|
||||
"prefs.general.minutes": "Phút",
|
||||
"prefs.general.name_setup": "Đặt tên cấu hình này",
|
||||
"prefs.general.profile_name": "Tên hồ sơ",
|
||||
"prefs.general.save_new": "Lưu thành mới",
|
||||
"prefs.general.step_import": "STEP và nhập",
|
||||
"prefs.general.this_computer": "Máy tính này",
|
||||
"prefs.general_note": "Hồ sơ, ngôn ngữ, tệp và cập nhật.",
|
||||
"prefs.gesture_orbit": "Xoay góc nhìn",
|
||||
"prefs.gesture_pan": "Trượt góc nhìn",
|
||||
"prefs.gesture_zoom": "Thu phóng",
|
||||
@@ -2255,10 +2338,10 @@
|
||||
"prefs.logo_accent": "Màu nhấn",
|
||||
"prefs.logo_theme": "Màu của chủ đề",
|
||||
"prefs.max_file_size": "Kích thước tệp tối đa để mở",
|
||||
"prefs.max_file_size_design": "Tệp thiết kế",
|
||||
"prefs.max_file_size_design": "Tệp lớn nhất có thể mở: Thiết kế",
|
||||
"prefs.max_file_size_flag": "Lần chạy này bắt đầu với --max-file-size {size}, có hiệu lực đến khi đóng SolidCraft. Các cài đặt này áp dụng khi khởi động không có tùy chọn đó.",
|
||||
"prefs.max_file_size_note": "Mặc định: 512 MB và 256 MB; tối đa 16 GB. Mở tệp cần lượng bộ nhớ gấp nhiều lần kích thước tệp. Thiết kế giữ dữ liệu nhập lớn có thể cần giới hạn tệp thiết kế cao hơn để lưu và mở lại.",
|
||||
"prefs.max_file_size_step": "Tệp STEP và tệp nhập",
|
||||
"prefs.max_file_size_step": "Tệp lớn nhất có thể mở: STEP và nhập",
|
||||
"prefs.max_file_size_web": "Ứng dụng web mở tệp STEP và tệp nhập tối đa 512 MB, thiết kế tối đa 256 MB: thẻ trình duyệt không chứa được tệp lớn hơn. Ứng dụng máy tính cho phép tăng các giới hạn này.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "Biểu tượng lớn",
|
||||
@@ -2275,7 +2358,7 @@
|
||||
"prefs.navigator_fit_note": "Nhấp vào một mặt, góc hoặc mũi tên của khối lập phương, hoặc một trục của gizmo, cũng nhìn vào tâm mô hình từ một khoảng cách duy nhất chứa trọn nó từ mọi phía (khối cầu bao). Khi bật, góc nhìn chuẩn (Home) dùng cùng mức thu phóng và tâm đó, nên các góc nhìn chọn sau chỉ xoay. Tắt: góc nhìn xoay tại chỗ, giữ nguyên điểm nhìn và mức thu phóng.",
|
||||
"prefs.network_host_remove": "Xóa",
|
||||
"prefs.network_locations": "Cho phép các vị trí mạng trong thiết kế",
|
||||
"prefs.network_locations_note": "Khi tắt: các tệp liên kết của thiết kế trên máy tính khác (\\\\server\\share) chỉ được mở từ các máy chủ được liệt kê tại đây, hoặc sau khi bạn đồng ý khi được hỏi.",
|
||||
"prefs.network_locations_note": "Dành cho tổ chức lưu thiết kế trên máy chủ tệp. Khi tắt: chỉ các máy chủ bạn cho phép.",
|
||||
"prefs.network_locations_section": "Vị trí mạng",
|
||||
"prefs.not_on_gpu": "GPU này không hỗ trợ",
|
||||
"prefs.on": "Bật",
|
||||
@@ -2301,10 +2384,12 @@
|
||||
"prefs.profile_applied": "Đã áp dụng hồ sơ.",
|
||||
"prefs.profile_changed": "Cài đặt đã thay đổi kể từ khi lưu hồ sơ. “Lưu” sẽ ghi chúng vào hồ sơ.",
|
||||
"prefs.profile_deleted": "Đã xóa hồ sơ.",
|
||||
"prefs.profile_export": "Xuất tệp…",
|
||||
"prefs.profile_export": "Xuất",
|
||||
"prefs.profile_exported": "Đã xuất hồ sơ.",
|
||||
"prefs.profile_imported": "Đã nhập hồ sơ. Chọn hồ sơ trong “Hồ sơ đang dùng” để chuyển sang.",
|
||||
"prefs.profile_imported": "Đã nhập hồ sơ.",
|
||||
"prefs.profile_name_hint": "Tên hồ sơ, ví dụ Laptop",
|
||||
"prefs.profile_operations": "Đo thời gian thao tác",
|
||||
"prefs.profile_operations_note": "Ghi lại thời gian các lệnh gọi SMLib, thao tác kernel, đặc trưng, lệnh và lần nhập. Tác tử đọc bằng diagnostics.profile.",
|
||||
"prefs.profile_rename": "Đổi tên",
|
||||
"prefs.profile_renamed": "Đã đổi tên hồ sơ.",
|
||||
"prefs.profile_save": "Lưu",
|
||||
@@ -2323,16 +2408,20 @@
|
||||
"prefs.render_previous_geometry_budget": "Dùng ngân sách hình học trước đây cho bản xem trước kết xuất",
|
||||
"prefs.render_previous_geometry_budget_help": "Khôi phục giới hạn hình học cố định trước đây (512 MiB trên máy tính 64 bit, 128 MiB trên các nền tảng khác) thay cho ngân sách dựa trên thiết bị.",
|
||||
"prefs.reports_saved": "Đã lưu {n}",
|
||||
"prefs.restore_page_defaults": "Khôi phục mặc định của trang",
|
||||
"prefs.ribbon_click_ends_tool": "Nhấp vào nút của công cụ đang chạy sẽ kết thúc nó",
|
||||
"prefs.ribbon_click_ends_tool_note": "Nhấp vào nút ruy-băng đang sáng của công cụ phác thảo đang dùng sẽ kết thúc công cụ, để các cú nhấp trong khung nhìn lại chọn đối tượng. Tắt: cú nhấp khởi động lại công cụ.",
|
||||
"prefs.ribbon_reveal": "Hiện kết quả tìm kiếm trên ribbon",
|
||||
"prefs.ribbon_reveal_note": "Khi rê chuột lên một kết quả trong Tìm lệnh, ribbon cho biết lệnh nằm ở đâu: tab, nút hoặc dòng của nó trong menu thả xuống của một bảng. Lệnh bắt đầu từ tìm kiếm sẽ nhấp nháy ở đó, và lệnh đang chạy từ menu thả xuống sẽ đánh dấu bảng của nó.",
|
||||
"prefs.right_click": "Nhấp chuột phải",
|
||||
"prefs.saving": "Tự động lưu và khôi phục",
|
||||
"prefs.saving": "Khởi động và lưu",
|
||||
"prefs.scale_note": "Cũng có thể dùng Ctrl + / Ctrl − trong cửa sổ. Các nút có kích thước 28 px ở mức 100%.",
|
||||
"prefs.scale_text": "Tỉ lệ và văn bản",
|
||||
"prefs.search": "Tìm kiếm cài đặt",
|
||||
"prefs.search_empty": "Không có cài đặt nào khớp",
|
||||
"prefs.section_hatch": "Gạch mặt cắt",
|
||||
"prefs.section_hatch_by_part": "Theo chi tiết",
|
||||
"prefs.section_hatch_one": "Một kiểu",
|
||||
"prefs.select": "Chọn",
|
||||
"prefs.select_one_item": "Chọn một mục",
|
||||
"prefs.select_other_hidden": "Chọn hình học ẩn bằng Chọn đối tượng khác",
|
||||
@@ -2447,6 +2536,12 @@
|
||||
"prefs.under_cursor": "Dưới con trỏ chuột",
|
||||
"prefs.units_precision": "Đơn vị và độ chính xác",
|
||||
"prefs.updates": "Cập nhật ứng dụng",
|
||||
"prefs.validation": "Kiểm tra thân",
|
||||
"prefs.validation.each_feature": "Một lần mỗi đặc trưng",
|
||||
"prefs.validation.each_step": "Sau mỗi bước",
|
||||
"prefs.validation.label": "Kiểm tra kernel đầy đủ",
|
||||
"prefs.validation.off": "Tắt (thử nghiệm)",
|
||||
"prefs.validation_note": "Sau mỗi bước: kiểm tra từng lần quét, phép boolean và đường bo.\nMột lần mỗi đặc trưng: kiểm tra kết quả của từng đặc trưng một lần.\nTắt (thử nghiệm): không kiểm tra.",
|
||||
"prefs.value_boxes_in_view": "Hiện giá trị cạnh tay nắm trong khung nhìn",
|
||||
"prefs.value_boxes_in_view_tip": "Giá trị của lệnh (Khoảng cách, Bán kính…) trong một ô cạnh tay nắm, phía trên mô hình. Tắt: bạn nhập giá trị trong cửa sổ lệnh, cửa sổ này nhận bàn phím sau khi chọn, và không có gì che mô hình.",
|
||||
"prefs.view": "Hành vi góc nhìn",
|
||||
@@ -2481,6 +2576,17 @@
|
||||
"recovery.just_now": "vừa xong",
|
||||
"recovery.min_ago": "{count} phút trước",
|
||||
"recovery.never_saved": "chưa từng lưu",
|
||||
"refused.change": "Không thể thay đổi {body}.",
|
||||
"refused.cut": "Không thể cắt {body}.",
|
||||
"refused.face_curved": "Một trong các mặt cong của nó không dùng được để mô hình hóa.",
|
||||
"refused.face_flat": "Một trong các mặt phẳng của nó không dùng được để mô hình hóa.",
|
||||
"refused.face_freeform": "Một trong các mặt tự do của nó không dùng được để mô hình hóa.",
|
||||
"refused.face_rounded": "Một trong các mặt bo tròn của nó không dùng được để mô hình hóa.",
|
||||
"refused.faces_apart": "Các mặt của nó không gặp nhau đủ khít để dùng cho mô hình hóa.",
|
||||
"refused.intersect": "Không thể giao với {body}.",
|
||||
"refused.join": "Không thể nối vào {body}.",
|
||||
"refused.repair": "Sửa chi tiết",
|
||||
"refused.show_face": "Hiện mặt",
|
||||
"render.adaptive_absolute": "Ngưỡng lỗi RGB tuyệt đối",
|
||||
"render.adaptive_coverage": "Mục tiêu lỗi độ phủ (Coverage error)",
|
||||
"render.adaptive_enable": "Phân bổ mẫu bằng các mẫu thử độc lập",
|
||||
@@ -3948,11 +4054,20 @@
|
||||
"the section's profiles must be in one sketch": "các biên dạng của mặt cắt phải nằm trong cùng một phác thảo",
|
||||
"the two origins are on the same component": "hai gốc tọa độ nằm trên cùng một thành phần",
|
||||
"thread.automatic_size": "Kích thước tự động",
|
||||
"thread.chamfer_depth_needed": "bắt buộc với dạng này",
|
||||
"thread.designation": "Ký hiệu ren",
|
||||
"thread.direction": "Chiều ren",
|
||||
"thread.drill_range": "Mũi khoan của nhà sản xuất taro là {drill} mm. Đường kính trong tiêu chuẩn của ren trong là {minor} mm. Chấp nhận mũi khoan từ {low} đến {high} mm.",
|
||||
"thread.drill_range_custom": "Mũi khoan bằng đường kính ngoài trừ bước ren, {drill} mm. Đường kính trong tiêu chuẩn của ren trong là {minor} mm. Chấp nhận mũi khoan từ {low} đến {high} mm.",
|
||||
"thread.drill_range_taper": "Mũi khoan của nhà sản xuất taro là {drill} mm. Đường kính trong theo tiêu chuẩn tại nơi ren kết thúc là {minor} mm. Chấp nhận mũi khoan từ {low} đến {high} mm.",
|
||||
"thread.end_square": "Vuông",
|
||||
"thread.fade_short": "1,25 P (ISO 3508)",
|
||||
"thread.fade_standard": "2,5 P (ISO 3508)",
|
||||
"thread.fits_the_face": "vừa với mặt, ví dụ {example}",
|
||||
"thread.form_bspp": "Ống G (ISO 228-1)",
|
||||
"thread.form_bspt": "Ống côn R, Rc (ISO 7-1)",
|
||||
"thread.form_npt": "Ống côn NPT (ASME B1.20.1)",
|
||||
"thread.form_rp": "Ống Rp (ISO 7-1)",
|
||||
"thread.iso_metric": "Hệ mét ISO",
|
||||
"thread.left_hand": "Ren trái",
|
||||
"thread.right_hand": "Ren phải",
|
||||
"thread.select_size": "Chọn kích thước",
|
||||
"timeline.delete_after_marker_one": "Xóa 1 tính năng sau điểm đánh dấu lịch sử",
|
||||
"timeline.delete_after_marker_other": "Xóa {count} tính năng sau điểm đánh dấu lịch sử",
|
||||
@@ -4126,6 +4241,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Chạy lệnh này trong cửa sổ dòng lệnh, sau đó nhấp Kết nối lại:",
|
||||
"ui.agent_chat_auth_title": "Đăng nhập vào AI Agent",
|
||||
"ui.agent_chat_cancel": "Hủy bỏ",
|
||||
"ui.agent_chat_cancel_timeout": "Tác nhân không phản hồi yêu cầu Dừng nên đã bị ngắt kết nối. Hãy kết nối lại để tiếp tục.",
|
||||
"ui.agent_chat_cannot_start": "Đã cài đặt nhưng không khởi động được: {error}",
|
||||
"ui.agent_chat_changes": "Thay đổi: {n}, trong 1 bước hoàn tác",
|
||||
"ui.agent_chat_changes_n": "{n} thay đổi",
|
||||
@@ -4203,6 +4319,7 @@
|
||||
"ui.agent_chat_no_images": "AI Agent này không hỗ trợ tiếp nhận hình ảnh",
|
||||
"ui.agent_chat_no_picture": "Không có hình ảnh trong bộ nhớ tạm",
|
||||
"ui.agent_chat_no_tool_call": "Mô hình không sử dụng các công cụ của SolidCraft nên thiết kế không thay đổi. Hãy chọn mô hình hỗ trợ gọi công cụ; xem mục Mô hình cục bộ trong hướng dẫn trò chuyện AI.",
|
||||
"ui.agent_chat_node_too_old": "Node.js {version} quá cũ đối với tác nhân này. Hãy cài đặt Node.js 20.10 trở lên, rồi nhấn Làm mới.",
|
||||
"ui.agent_chat_none_found": "Không tìm thấy tác tử lập trình nào. Hãy cài đặt một trong các tác tử sau, rồi nhấn Làm mới:",
|
||||
"ui.agent_chat_none_found_short": "Không tìm thấy tác tử nào",
|
||||
"ui.agent_chat_not_installed": "Chưa cài đặt",
|
||||
@@ -4422,6 +4539,8 @@
|
||||
"ui.centerline": "Đường tâm",
|
||||
"ui.chain": "Chuỗi",
|
||||
"ui.chamfer": "Vát mép",
|
||||
"ui.chamfer_angle": "Góc vát",
|
||||
"ui.chamfer_depth": "Chiều sâu vát",
|
||||
"ui.chamfer_type": "Kiểu vát mép",
|
||||
"ui.change_workspace": "Đổi không gian làm việc",
|
||||
"ui.circle": "Hình tròn",
|
||||
@@ -4452,10 +4571,16 @@
|
||||
"ui.colour_vision_sample": "Mẫu thử",
|
||||
"ui.colour_vision_tritan": "Mù màu xanh dương (Tritan)",
|
||||
"ui.colour_vision_tritan_tip": "Dành cho người cảm nhận màu xanh dương yếu hoặc mất hẳn",
|
||||
"ui.comb_length": "Độ dài lược",
|
||||
"ui.comb_length_tip": "Độ dài của các răng: bội số của độ dài tự động, độ dài này làm răng dài nhất bằng một phần năm kích thước các đường cong được hiển thị.",
|
||||
"ui.comb_teeth": "Răng",
|
||||
"ui.comb_teeth_tip": "Số răng của mỗi lược, cách đều dọc theo đường cong. Càng nhiều răng càng thấy rõ độ cong thay đổi thế nào.",
|
||||
"ui.combine": "Hợp nhất / Cắt khối",
|
||||
"ui.coming_later": "sắp ra mắt",
|
||||
"ui.command_done": "Hoàn tất lệnh {command}",
|
||||
"ui.command_palette_hint": "Nhập một lệnh, ví dụ: {example} — hoặc nhấn S để tìm kiếm",
|
||||
"ui.components_from_bodies_hint": "Tạo một thành phần cho mỗi vật thể đã chọn, đặt tên theo vật thể đó, bên dưới thành phần đang hoạt động.",
|
||||
"ui.components_from_bodies_required": "Chọn ít nhất một vật thể.",
|
||||
"ui.concentric": "Đồng tâm",
|
||||
"ui.cone": "Hình nón",
|
||||
"ui.conic_curve": "Đường cong Conic",
|
||||
@@ -4474,11 +4599,24 @@
|
||||
"ui.construction.ribbon_label": "Đường phụ",
|
||||
"ui.construction.side_hint": "Đường dựng hình: nhấp phía mà đường kéo dài tới; nhấp chuột phải để bắt đầu lại",
|
||||
"ui.context_menu": "Menu ngữ cảnh",
|
||||
"ui.continuity_crease": "Nếp gập",
|
||||
"ui.continuity_curvature": "Độ cong",
|
||||
"ui.continuity_curvature_change": "Thay đổi độ cong",
|
||||
"ui.continuity_curvature_change_tip": "Thay đổi độ cong (G3): độ cong cũng thay đổi trơn qua đường nối.",
|
||||
"ui.continuity_curvature_tip": "Liên tục độ cong (G2): phản chiếu đi qua mà không bị gãy.",
|
||||
"ui.continuity_g0": "Nếp gập G0",
|
||||
"ui.continuity_g1": "G1 tiếp tuyến",
|
||||
"ui.continuity_g2": "G2 độ cong",
|
||||
"ui.continuity_gap": "Khe hở",
|
||||
"ui.continuity_position": "Vị trí liên tục",
|
||||
"ui.continuity_position_tip": "Vị trí (G0): các bề mặt gặp nhau, tạo nếp gấp tại nơi đó.",
|
||||
"ui.continuity_smooth_curvature_change": "Thay đổi độ cong trơn",
|
||||
"ui.continuity_smooth_curvature_change_tip": "Thay đổi độ cong trơn (G4): ngay cả cách độ cong thay đổi cũng liền mạch qua đường nối.",
|
||||
"ui.continuity_tangent": "Tiếp tuyến",
|
||||
"ui.continuity_tangent_tip": "Tiếp tuyến (G1): không có nếp gấp, nhưng độ cong nhảy qua đường nối.",
|
||||
"ui.copy": "Sao chép",
|
||||
"ui.copy_details": "Sao chép chi tiết",
|
||||
"ui.copy_details_tip": "Sao chép toàn bộ thông báo để tra cứu hoặc báo cáo.",
|
||||
"ui.corner": "Góc",
|
||||
"ui.corners": "Các góc",
|
||||
"ui.cosmetic_thread_markings": "Ký hiệu ren quy ước",
|
||||
@@ -4596,8 +4734,10 @@
|
||||
"ui.edit_sketch_empty": "Không có phác thảo để chỉnh sửa",
|
||||
"ui.ellipse": "Hình elip",
|
||||
"ui.emboss_wrap_note": "Các biên dạng đường trực giao tiếp xúc với một hình trụ được hỗ trợ sẽ tự động quấn. Chọn một thành để chọn mục tiêu.",
|
||||
"ui.end": "Đầu",
|
||||
"ui.end_radius": "Bán kính kết thúc",
|
||||
"ui.end_scale": "Tỷ lệ đầu cuối",
|
||||
"ui.ends": "Các đầu",
|
||||
"ui.entities": "Các thực thể",
|
||||
"ui.equal": "Bằng nhau",
|
||||
"ui.equation_end": "Kết thúc",
|
||||
@@ -4617,6 +4757,7 @@
|
||||
"ui.export_ellipsis_keymap": "Xuất tệp…",
|
||||
"ui.export_keymap_tip": "Xuất phím tắt, danh sách S box yêu thích và thanh công cụ ra tệp",
|
||||
"ui.extend": "Kéo dài đường",
|
||||
"ui.extent": "Phạm vi",
|
||||
"ui.extent_type": "Kiểu giới hạn kéo dài",
|
||||
"ui.extract_component": "Trích xuất ra tệp riêng…",
|
||||
"ui.extract_design_changed": "Thiết kế đã thay đổi trong khi đang chọn tệp. Vui lòng thực hiện lại lệnh Trích xuất ra tệp.",
|
||||
@@ -4625,11 +4766,39 @@
|
||||
"ui.extrude_auto_to_object": "Đùn khối đến đối tượng được chọn",
|
||||
"ui.extrude_auto_to_object_tip": "Trong lệnh Đùn, khi đã chọn biên dạng, nhấp chọn một mặt, đỉnh hoặc mặt phẳng song song ngoài mặt phẳng của nó sẽ tự động chuyển phạm vi đùn thành 'Đến đối tượng' (To Object)",
|
||||
"ui.factor": "Hệ số",
|
||||
"ui.fade": "Thoát ren",
|
||||
"ui.fade_length": "Chiều dài thoát ren",
|
||||
"ui.feature_history": "Lịch sử tính năng",
|
||||
"ui.feature_options": "Tùy chọn tính năng",
|
||||
"ui.features": "Các tính năng",
|
||||
"ui.file": "Tệp",
|
||||
"ui.fillet": "Bo tròn mép",
|
||||
"ui.fillet_corner_ball": "Bi lăn",
|
||||
"ui.fillet_corner_ball_tip": "Một phần mặt cầu của bi lăn, tiếp tuyến (G1) với từng đường bo.",
|
||||
"ui.fillet_corner_curvature_tip": "Độ cong (G2): một mảng trơn. Góc được lùi dọc theo mỗi cạnh một khoảng Lùi ({factor} × bán kính nếu bạn không nhập) rồi được lấp bằng một mảng, nên phản chiếu đi qua mà không bị gãy.",
|
||||
"ui.fillet_corner_needs_constant": "Góc mảng trơn (G2) cần Bán kính không đổi và Khớp chính xác.",
|
||||
"ui.fillet_corner_patch": "Mảng trơn",
|
||||
"ui.fillet_corner_patch_tip": "Liên tục độ cong (G2): góc được lùi dọc theo mỗi cạnh và được lấp bằng một mảng, nên phản chiếu đi qua mà không bị gãy.",
|
||||
"ui.fillet_corner_tangent_tip": "Tiếp tuyến (G1): góc bi lăn, một phần mặt cầu của bi lăn tiếp tuyến với từng đường bo.",
|
||||
"ui.fillet_error_corner": "Không thể tạo góc trơn tại đây. Hãy thử khoảng Lùi khác hoặc góc Tiếp tuyến.",
|
||||
"ui.fillet_error_failed": "Không thể tạo đường bo này trên các cạnh này. Hãy thử bán kính khác hoặc bo ít cạnh hơn một lần.",
|
||||
"ui.fillet_error_setback_short": "Khoảng lùi quá ngắn cho mảng ở góc này: góc dựng được từ {least} trở lên. Hãy thử một khoảng Lùi dài hơn.",
|
||||
"ui.fillet_error_too_large": "Bán kính quá lớn so với các mặt bên cạnh những cạnh này. Hãy thử bán kính nhỏ hơn hoặc đặt Giới hạn mặt thành Bán kính lớn nhất.",
|
||||
"ui.fillet_fit": "Giới hạn mặt",
|
||||
"ui.fillet_fit_exact": "Bán kính chính xác",
|
||||
"ui.fillet_fit_needs_constant": "Khớp bán kính cần Bán kính không đổi.",
|
||||
"ui.fillet_fit_reduce": "Bán kính lớn nhất",
|
||||
"ui.fillet_fit_taper": "Bán kính thu hẹp",
|
||||
"ui.fillet_fit_tip": "Khi bán kính lớn hơn mức các mặt bên cạnh một cạnh cho phép.\nBán kính chính xác: đúng bán kính đã nhập, hoặc không bo gì và có thông báo lý do.\nBán kính lớn nhất: mọi cạnh được bo với bán kính lớn nhất vừa khớp.\nBán kính thu hẹp: giữ bán kính, đường bo chỉ thu hẹp ở nơi các mặt hết chỗ.",
|
||||
"ui.fillet_options_tip": "Chuỗi tiếp tuyến: bo cả các cạnh nối tiếp trơn từ những cạnh đã chọn.\nXem trước: hiển thị kết quả trong khung nhìn khi bạn chỉnh sửa.",
|
||||
"ui.fillet_setback": "Lùi",
|
||||
"ui.fillet_setback_hint": "{factor} × bán kính",
|
||||
"ui.fillet_setback_least": "Lớn hơn {least}",
|
||||
"ui.fillet_setback_tip": "Nơi mảng góc trơn bắt đầu: khoảng cách dọc theo mỗi cạnh tính từ góc. Mặc định là {factor} × bán kính. Giá trị phải lớn hơn khoảng lùi nhỏ nhất, tại đó các đường bo gặp nhau mà không cần lùi: bằng bán kính khi các mặt vuông góc với nhau.",
|
||||
"ui.fillet_smooth_radius": "Thay đổi bán kính trơn",
|
||||
"ui.fillet_type_choices_tip": "Bán kính không đổi: một bán kính dọc suốt cạnh.\nĐộ dài dây cung: độ rộng ngang đường bo giữ nguyên, bất kể góc gặp nhau của các mặt.\nBán kính thay đổi: một bán kính ở mỗi đầu, và thêm dọc cạnh nếu bạn thêm vào.\nLiên tục độ cong: mặt cắt không làm gãy phản chiếu tại nơi gặp các mặt phẳng (G2).\nSpline: bán kính dọc cạnh theo các bán kính điều khiển bạn đặt.",
|
||||
"ui.fillet_type_curvature": "Liên tục độ cong",
|
||||
"ui.fillet_type_tip": "Mặt cắt ngang qua cạnh. Liên tục độ cong: mặt cắt liên tục độ cong (G2) giữa các mặt phẳng, nên phản chiếu đi từ các mặt vào đường bo mà không bị gãy.",
|
||||
"ui.filter_commands_or_keys": "Lọc lệnh hoặc phím tắt",
|
||||
"ui.findInTimeline": "Tìm trong lịch sử thiết kế",
|
||||
"ui.findInWindow": "Tìm trong cửa sổ",
|
||||
@@ -4761,6 +4930,7 @@
|
||||
"ui.history_sketch_listed_under": "Được liệt kê dưới {feature}",
|
||||
"ui.history_title": "Lịch sử",
|
||||
"ui.history_toggle_names": "Hiện hoặc ẩn tên tính năng",
|
||||
"ui.history_waits_for_editor": "Hoàn tác, Làm lại và Lịch sử sẽ chờ cho đến khi trình chỉnh sửa tính năng mở xong",
|
||||
"ui.hole": "Tạo lỗ",
|
||||
"ui.hole_tap_type": "Kiểu ta-rô ren lỗ",
|
||||
"ui.hole_type": "Loại lỗ",
|
||||
@@ -4778,6 +4948,7 @@
|
||||
"ui.inspect": "Kiểm tra",
|
||||
"ui.instances": "Số bản",
|
||||
"ui.intersect": "Giao nhau",
|
||||
"ui.iso_4753": "ISO 4753",
|
||||
"ui.isolate": "Cô lập đối tượng",
|
||||
"ui.join": "Nối liền",
|
||||
"ui.joint_play": "▶ Phát",
|
||||
@@ -4880,6 +5051,7 @@
|
||||
"ui.part_size_needs_a_part": "Hãy chọn một chi tiết tiêu chuẩn đã chèn (vít, đai ốc, vòng đệm hoặc chốt) để đổi kích thước. Để phóng to hoặc thu nhỏ một thân khối, hãy dùng Thay đổi tỷ lệ.",
|
||||
"ui.part_size_shared": "Dựng lại chi tiết, thay thế mọi tính năng đã thêm. Tất cả các lần xuất hiện của chi tiết này đều được cập nhật.",
|
||||
"ui.paste": "Dán",
|
||||
"ui.patch_continuity_corner_tip": "Mảng gặp các mặt xung quanh với độ liên tục đã chọn dọc theo mỗi cạnh. Tại góc nơi các mặt đó gặp nhau với độ cong khác nhau, mảng giữ tiếp tuyến (G1) ở đó.",
|
||||
"ui.path": "Đường dẫn",
|
||||
"ui.pattern_object_type_tip": "Thân khối: các hình đặc, chọn trong khung nhìn hoặc Trình duyệt; mỗi thân được sao chép nguyên vẹn.\nTính năng: các bước trong Lịch sử, như một lần đùn khối hay một lỗ, chọn trong Lịch sử hoặc trên một mặt do chúng tạo ra; bước đó được lặp lại.\nBản phác thảo không phải là cả hai: lặp lại các đường cong của nó bằng Mảng hoặc Lấy đối xứng trong chính bản phác thảo, hoặc đùn khối nó trước.",
|
||||
"ui.pattern_on_path": "Mảng dọc theo đường dẫn",
|
||||
@@ -5056,6 +5228,8 @@
|
||||
"ui.sketch_intersect_geometry": "Hình học",
|
||||
"ui.sketch_intersect_projection_link": "Liên kết chiếu",
|
||||
"ui.sketch_intersect_selection_filter": "Bộ lọc lựa chọn",
|
||||
"ui.sketch_keys_only": "Không dùng phím mô hình trong bản phác thảo",
|
||||
"ui.sketch_keys_only_tip": "Khi đang chỉnh sửa bản phác thảo, chỉ các phím phác thảo và các phím dùng được ở mọi nơi mới hoạt động; phím mô hình như E hoặc F không làm gì, nên một phím bấm nhầm không thể đóng một bản phác thảo nặng.",
|
||||
"ui.sketch_modifiers": "Phím bổ trợ phác thảo",
|
||||
"ui.sketch_project_geometry": "Hình học",
|
||||
"ui.sketch_report.all_constrained": "Mọi thứ đã được ràng buộc hoàn toàn.",
|
||||
@@ -5073,7 +5247,7 @@
|
||||
"ui.slicer_no_response": "{slicer} không phản hồi kịp thời. Tệp đã được lưu tại {path}: hãy gửi lại sau khi phần mềm cắt lớp khởi động xong.",
|
||||
"ui.slicer_resend_hint": "Gửi lại sẽ thay thế tệp đang mở trong phần mềm cắt lớp.",
|
||||
"ui.slicer_single_instance": "Mở tệp trong phần mềm cắt lớp đang chạy sẵn",
|
||||
"ui.slicer_single_instance_tip": "PrusaSlicer, SuperSlicer và Cura sẽ tải tệp vào cửa sổ đang mở thay vì khởi động cửa sổ mới. Các phần mềm cắt lớp khác được khởi động như bình thường. PrusaSlicer 3.0 luôn hoạt động theo cách này: trình khởi chạy của nó không thể nhận tệp theo cách nào khác.",
|
||||
"ui.slicer_single_instance_tip": "Dành cho phần mềm cắt lớp tải tệp vào cửa sổ đang chạy.",
|
||||
"ui.slicer_starting": "Đang khởi động {slicer}…",
|
||||
"ui.slicer_still_starting": "Vẫn đang chờ {slicer} khởi động. Chưa có dữ liệu nào khác được gửi.",
|
||||
"ui.slicer_stl_fallback": "Không thể ghi tệp 3MF nên định dạng xuất là STL.",
|
||||
@@ -5218,13 +5392,13 @@
|
||||
"update.available": "Đã có phiên bản SolidCraft {version}",
|
||||
"update.button": "Cập nhật",
|
||||
"update.button_version": "Cập nhật lên {version}",
|
||||
"update.check_automatically": "Tự động kiểm tra bản cập nhật",
|
||||
"update.check_automatically": "Kiểm tra khi khởi động và mỗi ngày một lần",
|
||||
"update.check_for_updates": "Kiểm tra bản cập nhật",
|
||||
"update.check_now": "Kiểm tra ngay",
|
||||
"update.check_unavailable": "Không thể kiểm tra bản cập nhật cho bản cài đặt này.",
|
||||
"update.checking": "Đang kiểm tra…",
|
||||
"update.copy": "Sao chép",
|
||||
"update.dmg_opened": "Ảnh đĩa đã mở và được xác thực: kéo SolidCraft vào thư mục Applications để thay thế bản cũ, sau đó khởi động lại SolidCraft.",
|
||||
"update.dmg_opened": "Ảnh đĩa đã mở và được xác thực: thoát SolidCraft, kéo SolidCraft mới vào thư mục Applications để thay thế bản cũ, sau đó mở lại SolidCraft.",
|
||||
"update.download": "Tải về",
|
||||
"update.download_open": "Tải về và mở",
|
||||
"update.downloading": "Đang tải về và xác thực… {done} trên {total} MB",
|
||||
|
||||
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "矩形",
|
||||
"Rectangular Tube": "矩形管",
|
||||
"Reference face": "參考面",
|
||||
"Reference plane": "參考平面",
|
||||
"Relief Depth": "止裂槽深度",
|
||||
"Relief Width": "止裂槽寬度",
|
||||
"Remove Tools": "移除工具",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "剖面",
|
||||
"Section (optional)": "剖面(可選)",
|
||||
"Section position": "截面位置",
|
||||
"Sections": "剖面",
|
||||
"Sections (one sketch each)": "剖面(每個一張草圖)",
|
||||
"Select Other": "選取其他",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "「選擇其他」:在模型上按住左鍵(或右鍵點擊 › 選擇其他…)列出游標下的所有物件;在同一位置再次點擊可選擇該處的下一個物件。雙擊中鍵轉到主檢視(偏好設定 › 導覽)。",
|
||||
@@ -859,6 +861,7 @@
|
||||
"cmd.component_extract": "提取到檔案",
|
||||
"cmd.component_finish_edit": "完成編輯",
|
||||
"cmd.component_from_bodies": "由實體建立元件",
|
||||
"cmd.component_hide_history": "隱藏元件歷程記錄",
|
||||
"cmd.component_insert_design": "插入設計",
|
||||
"cmd.component_links": "列出連結",
|
||||
"cmd.component_list": "列出元件",
|
||||
@@ -875,6 +878,7 @@
|
||||
"cmd.component_rename": "重新命名元件",
|
||||
"cmd.component_replace_with_link": "取代為連結",
|
||||
"cmd.component_revert_position": "還原位置",
|
||||
"cmd.component_show_history": "顯示元件歷程記錄",
|
||||
"cmd.component_unlock": "解鎖內容",
|
||||
"cmd.component_update_links": "更新連結",
|
||||
"cmd.config_activate": "啟用組態",
|
||||
@@ -885,6 +889,7 @@
|
||||
"cmd.config_rule": "組態規則",
|
||||
"cmd.config_rules": "組態規則",
|
||||
"cmd.config_table": "顯示組態表",
|
||||
"cmd.construct.plane.axis_point": "過軸與點的平面",
|
||||
"cmd.construct_axis_edge": "過邊的軸",
|
||||
"cmd.construct_axis_normal_to_face": "垂直於面的軸",
|
||||
"cmd.construct_axis_through_cylinder": "過圓柱/圓錐/圓環的軸",
|
||||
@@ -933,6 +938,11 @@
|
||||
"cmd.explode_show": "顯示爆炸圖",
|
||||
"cmd.expr_evaluate": "計算運算式",
|
||||
"cmd.face_delete": "刪除面",
|
||||
"cmd.file.export_3mf": "匯出 3MF",
|
||||
"cmd.file.export_obj": "匯出 OBJ",
|
||||
"cmd.file.export_step": "匯出 STEP",
|
||||
"cmd.file.export_stl": "匯出 STL",
|
||||
"cmd.file.export_urdf": "匯出 URDF",
|
||||
"cmd.file_export": "匯出",
|
||||
"cmd.file_insert_mesh": "插入網格",
|
||||
"cmd.file_insert_step": "插入 STEP",
|
||||
@@ -950,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "啟用元件顏色",
|
||||
"cmd.inspect_colored_components_disable": "停用元件顏色",
|
||||
"cmd.inspect_compare": "比較實體",
|
||||
"cmd.inspect_compare_picture": "與圖片比較",
|
||||
"cmd.inspect_continuity": "連續性分析",
|
||||
"cmd.inspect_curvature_comb": "曲率梳分析",
|
||||
"cmd.inspect_curvature_map": "曲率圖分析",
|
||||
@@ -1040,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "編輯點雲",
|
||||
"cmd.pointcloud_insert": "插入點雲",
|
||||
"cmd.pointcloud_list": "點雲",
|
||||
"cmd.render.compare.divider": "移動彩現比較分隔線",
|
||||
"cmd.render.result.discard": "捨棄彩現結果",
|
||||
"cmd.render.result.select": "選取彩現結果",
|
||||
"cmd.render_aging_describe": "分層表面老化",
|
||||
"cmd.render_backends": "彩現後端",
|
||||
"cmd.render_batch": "彩現所有鏡頭",
|
||||
@@ -1254,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "移動時間軸標記",
|
||||
"cmd.timeline_stop": "停止播放歷程記錄",
|
||||
"cmd.timeline_suppress": "抑制特徵",
|
||||
"cmd.ui.assistant": "切換助理",
|
||||
"cmd.view.render_browser": "彩現瀏覽器",
|
||||
"cmd.view.render_dock": "移動彩現停靠面板",
|
||||
"cmd.view.render_focus": "彩現聚焦檢視",
|
||||
@@ -1288,9 +1303,11 @@
|
||||
"cmd.view_tool": "導覽工具",
|
||||
"cmd.view_top": "俯視圖",
|
||||
"cmd.view_visual_style": "視覺樣式",
|
||||
"color.blue": "藍色",
|
||||
"color.copy": "點擊複製顏色值",
|
||||
"color.edit": "點擊編輯顏色",
|
||||
"color.gamma": "以 0–255 伽馬空間顯示顏色值",
|
||||
"color.green": "綠色",
|
||||
"color.hex": "十六進位",
|
||||
"color.hex_help": "輸入 #rrggbb 或 #rrggbbaa;六位數會保留不透明度",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1298,6 +1315,7 @@
|
||||
"color.linear": "以 0–1 線性空間顯示顏色值",
|
||||
"color.recent": "最近使用的顏色",
|
||||
"color.recent_help": "您在任何顏色欄位中選擇的顏色會顯示於此",
|
||||
"color.red": "紅色",
|
||||
"color.saturation": "飽和度(百分比)",
|
||||
"color.saturation_value": "飽和度和明度",
|
||||
"color.selected": "所選顏色",
|
||||
@@ -1305,6 +1323,8 @@
|
||||
"color.step_up": "增加",
|
||||
"color.value": "明度,即亮度(百分比)",
|
||||
"config.column_filter_empty": "沒有符合的欄",
|
||||
"config.rule_parameter_columns": "只會列出表格中有對應欄的參數。以「新增欄」(+) 加入一欄,即可在此使用。",
|
||||
"config.rule_suppression_columns": "只會列出表格中有「抑制」欄的特徵。以「新增欄」(+) 加入一欄,即可在此控制。",
|
||||
"contact.components_count": "{count} 個元件",
|
||||
"contact.enabled_label": "已啟用",
|
||||
"coupling.gear_1": "齒輪 1",
|
||||
@@ -1353,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "請選取實體或特徵:草圖兩者皆非。請用草圖自己的「陣列」或「鏡射」重複其曲線,或先將它擠出。",
|
||||
"dialog.select": "↖ 選擇",
|
||||
"dialog.selected_count": "↖ 已選擇 {count} 個",
|
||||
"dialog.selection_constraint": "拘束 {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · 曲線 {id}",
|
||||
"dialog.selection_guidance": "選擇輸入項進行修改。點擊已選物件可取消選擇;按住 Ctrl/Cmd 點擊也可切換選擇。檢查預覽後,再按「確定」。",
|
||||
"dialog.selection_midpoint": "中點 · {id}",
|
||||
"dialog.selection_profile": "{sketch} · 輪廓 {number}",
|
||||
"dialog.selection_step": "第 {step} 步,共 {total} 步:選擇{input}",
|
||||
"dialog.step": "步驟",
|
||||
"document.changed_on_disk": "{name} 已在磁碟上變更",
|
||||
@@ -1436,6 +1460,7 @@
|
||||
"help.construct.axis.two_points": "通過兩點、頂點或草圖點建立建構軸。用於穿過兩個已知位置的軸。",
|
||||
"help.construct.plane.along_path": "在邊或曲線上沿其某個位置,建立垂直於它的建構平面。用於為掃掠繪製輪廓草圖,或繪製路徑的剖面。",
|
||||
"help.construct.plane.at_angle": "以某個軸或邊為基準,將平面或面旋轉一個角度,建立建構平面。用於傾斜的草圖,例如斜角支架。",
|
||||
"help.construct.plane.axis_point": "建立包含一條軸與軸外一點的建構平面。依序選取原點或建構軸、直邊或草圖直線,再選取頂點、草圖點或建構點。軸上的點無法決定唯一的平面。",
|
||||
"help.construct.plane.midplane": "在兩個平面或平坦的面正中間建立建構平面;若兩者以一個角度相交,則平分該夾角。用作鏡射的對稱平面。",
|
||||
"help.construct.plane.offset": "建立與平面或平坦的面平行的建構平面,可指定距離或通過一點。用於在面上方的指定高度繪製草圖。",
|
||||
"help.construct.plane.perpendicular": "建立包含一條直線或邊、並垂直於某平面或面的建構平面。用於沿邊在模型上繪製直立的草圖。",
|
||||
@@ -1693,6 +1718,7 @@
|
||||
"help.timeline.delete": "從時間軸中刪除所選特徵;刪除草圖時,由它建立的特徵也會一併刪除。用於移除不再需要的步驟。",
|
||||
"help.timeline.dependents": "列出會隨某個特徵一起被刪除的特徵,以及沒有它就會失敗的特徵。用於刪除或變更特徵之前。",
|
||||
"help.timeline.play": "逐一重播其餘特徵,每次重建後暫停半秒。再次執行此命令或按 Escape 可停止;「移至結尾」會立即跳到結尾。",
|
||||
"help.version": "版本 {version}",
|
||||
"help.view.apply": "將檢視轉到標準方向(例如前、上或等角),或轉到已儲存的命名檢視。用於從固定角度觀看模型。",
|
||||
"help.view.delete": "刪除已儲存的命名檢視。不會更改模型。",
|
||||
"help.view.fit": "縮放並平移,使整個模型或所選項目填滿檢視。用於模型跑出視野之外時。",
|
||||
@@ -1913,6 +1939,8 @@
|
||||
"motion.track_name": "軌道 {number}",
|
||||
"nav.alias.help": "Shift+Alt+左鍵拖曳可環繞,Shift+Alt+中鍵拖曳可平移,Shift+Alt+右鍵拖曳可縮放;中鍵拖曳也可平移,右鍵拖曳也可環繞",
|
||||
"nav.blender.help": "中鍵拖曳可環繞,Shift+中鍵可平移,Ctrl+中鍵可縮放;右鍵拖曳不會移動相機",
|
||||
"nav.creo.help": "中鍵拖曳可環繞,Shift+中鍵可平移,Ctrl+中鍵垂直拖曳可縮放(向下為放大);捲動滾輪也可縮放",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "滑鼠與 3D 滑鼠可越過兩個極點旋轉並保留滾轉。Shift 軸向吸附已停用。",
|
||||
"nav.free_orbit.label": "自由環繞",
|
||||
"nav.free_orbit.snap_help": "滑鼠與 3D 滑鼠可越過兩個極點旋轉並保留滾轉。環繞拖曳時按住 Shift,可吸附檢視方向與滾轉。",
|
||||
@@ -2065,12 +2093,19 @@
|
||||
"parts.family.button_head_cap_screw": "內六角半圓頭螺絲",
|
||||
"parts.family.dowel_pin": "圓柱銷",
|
||||
"parts.family.hex_bolt": "六角頭螺栓",
|
||||
"parts.family.hex_cap_screw_inch": "外六角螺絲(英吋)",
|
||||
"parts.family.hex_nut": "六角螺帽",
|
||||
"parts.family.hex_nut_inch": "六角螺帽(英吋)",
|
||||
"parts.family.set_screw": "內六角平端止付螺絲",
|
||||
"parts.family.socket_head_cap_screw": "內六角圓柱頭螺絲",
|
||||
"parts.family.washer": "平墊圈",
|
||||
"parts.family.washer_sae": "平墊圈,SAE(窄型)",
|
||||
"parts.family.washer_uss": "平墊圈,USS(寬型)",
|
||||
"parts.holes": "孔(可選)",
|
||||
"parts.placement_hint": "選取一個或多個孔,將零件放入每個孔中,或按「確定」將其放在原點",
|
||||
"parts.system": "單位系統",
|
||||
"parts.system.inch": "英吋",
|
||||
"parts.system.metric": "公制",
|
||||
"pattern.alignment_2": "對齊 2",
|
||||
"pattern.alignment_hint": "數量包含原始物件。置中時副本分佈在兩側;數量為偶數時,反轉可切換多一個副本的一側。",
|
||||
"pattern.direction_1": "方向 1",
|
||||
@@ -2123,6 +2158,8 @@
|
||||
"prefs.bottom_centre": "下方置中",
|
||||
"prefs.bottom_right": "右下方",
|
||||
"prefs.browser": "瀏覽器",
|
||||
"prefs.browser_components_open": "元件初始展開",
|
||||
"prefs.browser_components_open_note": "瀏覽器中的每個元件都像以前一樣初始展開,而不是在您展開前保持摺疊。",
|
||||
"prefs.browser_eye_left": "眼睛圖示置於摺疊箭頭旁",
|
||||
"prefs.browser_eye_left_note": "瀏覽器各列會照舊把顯示/隱藏的眼睛圖示放在摺疊箭頭旁,而不是放在各列右端的一欄中。",
|
||||
"prefs.browser_reveal_on_hover": "游標移上時顯示",
|
||||
@@ -2132,6 +2169,8 @@
|
||||
"prefs.button_left": "左鍵拖曳",
|
||||
"prefs.button_middle": "中鍵拖曳",
|
||||
"prefs.button_right": "右鍵拖曳",
|
||||
"prefs.carousel_next": "下一個",
|
||||
"prefs.carousel_previous": "上一個",
|
||||
"prefs.choose_font": "選擇字型檔案…",
|
||||
"prefs.click": "點擊",
|
||||
"prefs.contact_shading": "柔和接觸陰影",
|
||||
@@ -2144,6 +2183,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "曲面",
|
||||
"prefs.curves": "曲面",
|
||||
"prefs.cut_preview_tint": "以紅色顯示移除的材料",
|
||||
"prefs.cut_preview_tint_help": "預覽會以紅色半透明的虛影填滿特徵所移除的材料,以及被它取代的形狀。關閉時,這些填色完全透明,只繪製輪廓線。",
|
||||
"prefs.dark_parts_shading": "保留深色零件的形狀",
|
||||
"prefs.dark_parts_shading_help": "以線性光搭配柔和的天空補光對檢視進行著色,讓深色的面保有漸層,而不會變成全黑",
|
||||
"prefs.dead_zone_note": "斜線區域為死區:此範圍內的移動會被忽略。",
|
||||
@@ -2202,6 +2243,8 @@
|
||||
"prefs.interface_language": "介面語言",
|
||||
"prefs.interface_note": "SolidCraft 的外觀與各面板的位置。",
|
||||
"prefs.interval_minutes": "{n} 分鐘",
|
||||
"prefs.invert_curved_roll_arrows": "反轉彎曲的滾轉箭頭",
|
||||
"prefs.invert_curved_roll_arrows_note": "立方體角落的兩個彎曲箭頭會在螢幕平面內滾轉檢視。開啟時:順時針箭頭會逆時針滾轉,逆時針箭頭會順時針滾轉。關閉時:每個箭頭依其彎曲的方向滾轉檢視。",
|
||||
"prefs.invert_navigator_arrows": "反轉檢視立方體的旋轉箭頭",
|
||||
"prefs.invert_navigator_arrows_note": "立方體周圍的箭頭會依其所指方向環繞相機,與 Fusion 和 Inventor 相同:左側箭頭指向右方,並將相機向右擺動。關閉時:箭頭會將立方體轉向它所在的一側,因此左側箭頭會將相機向左擺動。彎曲的滾轉箭頭不受影響。",
|
||||
"prefs.keep_work_on_tab_switch": "切換設計時保持對話方塊與工具開啟",
|
||||
@@ -2261,6 +2304,8 @@
|
||||
"prefs.placement": "放置",
|
||||
"prefs.prefer_free_sketch_points": "點重疊時優先選取可移動的草圖點",
|
||||
"prefs.preview_choices": "所選設定的預覽",
|
||||
"prefs.preview_tint_new_faces": "在預覽中為新增的表面著色",
|
||||
"prefs.preview_tint_new_faces_help": "會改變實體的特徵預覽,可用預覽色繪製其新增的表面。關閉時,實體依本身的樣子繪製。",
|
||||
"prefs.print_face_highlight": "列印底面醒目提示",
|
||||
"prefs.print_face_highlight_tip": "在檢視中為實體的列印底面著色及描邊的強度。0 % 不顯示醒目提示。",
|
||||
"prefs.profile_active": "作用中的設定檔",
|
||||
@@ -2558,6 +2603,10 @@
|
||||
"render.base_color": "基底顏色 · sRGB",
|
||||
"render.base_metallic": "金屬比例",
|
||||
"render.base_opaque_only": "此編輯器保留不透明實體的語意。光學穿透請使用專屬的玻璃控制項。",
|
||||
"render.batch_all_shots": "所有鏡頭({count})",
|
||||
"render.batch_empty": "彩現所有鏡頭前,請先儲存一個鏡頭。",
|
||||
"render.batch_limit": "「彩現所有鏡頭」最多只能處理 32 個鏡頭。",
|
||||
"render.batch_status": "{running} 個執行中 · {waiting} 個等待中",
|
||||
"render.bloom_enable": "顯示泛光",
|
||||
"render.bloom_help": "僅用於輸出的創意輝光,由線性正值高光經三次正規化方框模糊擴散而成。半徑以輸出像素為單位。在轉換為 sRGB 之前,於線性顯示光中疊加;並非鏡頭效果、體積效果,也無法改善收斂。原始 EXR 保持不變。",
|
||||
"render.bloom_opaque": "泛光需要不透明輸出。透明 PNG 會被拒絕,而不會默默壓平。",
|
||||
@@ -2668,6 +2717,13 @@
|
||||
"render.choose_look": "請先在「外觀」中選擇風格",
|
||||
"render.chromatic_offset": "邊緣色差偏移",
|
||||
"render.clear_stage": "移除已儲存的佈景",
|
||||
"render.compare_divider": "比較分隔線",
|
||||
"render.compare_live": "即時",
|
||||
"render.compare_live_preview": "即時預覽",
|
||||
"render.compare_render": "彩現 {number}",
|
||||
"render.compare_side": "彩現 {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} 秒 · {renderer} · 擷取於修訂版本 {revision}",
|
||||
"render.compare_swap": "對調",
|
||||
"render.copy_request": "複製 JSON",
|
||||
"render.cpu_aa_help": "空間取樣:取樣越多,邊緣越平滑,但彩現時間也越長。",
|
||||
"render.cpu_area_add": "新增矩形 CPU 燈光",
|
||||
@@ -3417,6 +3473,10 @@
|
||||
"render.result_retained": "顯示上一張已完成的影像;較新的彩現尚未取代它。",
|
||||
"render.result_sequence": "檢查點",
|
||||
"render.result_title": "彩現結果",
|
||||
"render.results_choose": "保留的結果",
|
||||
"render.results_label": "彩現 {number} · {shot} · {seconds} 秒",
|
||||
"render.results_stale_label": "{result} [已過期]",
|
||||
"render.results_unnamed": "影像",
|
||||
"render.return_design": "返回設計",
|
||||
"render.review_stale": "此檢閱已過期。請先解決或重新檢查,再套用。",
|
||||
"render.ribbon_aging": "老化",
|
||||
@@ -3900,10 +3960,7 @@
|
||||
"the two origins are on the same component": "這兩個原點位於同一個元件上",
|
||||
"thread.automatic_size": "自動尺寸",
|
||||
"thread.designation": "螺紋標示",
|
||||
"thread.direction": "螺紋方向",
|
||||
"thread.iso_metric": "ISO 公制",
|
||||
"thread.left_hand": "左旋",
|
||||
"thread.right_hand": "右旋",
|
||||
"thread.select_size": "選擇尺寸",
|
||||
"timeline.delete_after_marker_one": "刪除歷程標記之後的 1 個特徵",
|
||||
"timeline.delete_after_marker_other": "刪除歷程標記之後的 {count} 個特徵",
|
||||
@@ -4077,6 +4134,7 @@
|
||||
"ui.agent_chat_auth_terminal": "在終端中執行以下命令,然後再次點擊連線:",
|
||||
"ui.agent_chat_auth_title": "登入代理",
|
||||
"ui.agent_chat_cancel": "取消",
|
||||
"ui.agent_chat_cancel_timeout": "代理未回應「停止」,已中斷連線。請重新連線以繼續。",
|
||||
"ui.agent_chat_cannot_start": "已安裝,但無法啟動:{error}",
|
||||
"ui.agent_chat_changes": "{n} 項變更,一步復原",
|
||||
"ui.agent_chat_changes_n": "{n} 項變更",
|
||||
@@ -4086,11 +4144,13 @@
|
||||
"ui.agent_chat_command": "代理命令",
|
||||
"ui.agent_chat_connect": "連線",
|
||||
"ui.agent_chat_copy": "複製",
|
||||
"ui.agent_chat_copy_message": "複製訊息",
|
||||
"ui.agent_chat_ctx_bodies": "實體",
|
||||
"ui.agent_chat_ctx_elements": "元素",
|
||||
"ui.agent_chat_ctx_features": "特徵",
|
||||
"ui.agent_chat_ctx_more": "另有 {n} 項",
|
||||
"ui.agent_chat_ctx_other": "其他",
|
||||
"ui.agent_chat_custom_heading": "自訂模型",
|
||||
"ui.agent_chat_custom_hint": "任何透過標準輸入輸出使用 Agent Client Protocol 的程式。不經過 shell 執行。",
|
||||
"ui.agent_chat_denied": "已拒絕",
|
||||
"ui.agent_chat_deny": "拒絕",
|
||||
@@ -4134,8 +4194,16 @@
|
||||
"ui.agent_chat_mode_all": "全部",
|
||||
"ui.agent_chat_mode_ask": "詢問",
|
||||
"ui.agent_chat_mode_edits": "編輯",
|
||||
"ui.agent_chat_model_custom": "使用自訂:{model}",
|
||||
"ui.agent_chat_model_no_tools": "{model} 無法使用 SolidCraft 的工具,只能聊天。請選擇支援工具的模型。",
|
||||
"ui.agent_chat_model_rejected": "代理未接受 {model}:{error}",
|
||||
"ui.agent_chat_model_search": "搜尋模型…",
|
||||
"ui.agent_chat_model_tip": "代理使用的模型。您的選擇會為此代理記住。",
|
||||
"ui.agent_chat_models_failed": "無法列出模型:{error}",
|
||||
"ui.agent_chat_models_heading": "可用的模型",
|
||||
"ui.agent_chat_models_listing": "正在列出模型…",
|
||||
"ui.agent_chat_models_none": "端點未列出任何模型",
|
||||
"ui.agent_chat_models_not_a_list": "回應不是模型清單",
|
||||
"ui.agent_chat_new": "新對話",
|
||||
"ui.agent_chat_new_confirm": "開始新對話?目前對話將被清除。",
|
||||
"ui.agent_chat_next_picture": "下一張圖片",
|
||||
@@ -4409,14 +4477,21 @@
|
||||
"ui.construction.contact_hint": "切線:選取圓/圓弧或開放雲形線/圓錐曲線,再選取切點(自由曲線:端點)",
|
||||
"ui.construction.external_hint": "切線:選取圓或圓弧、一個外部點,再選取所需的解",
|
||||
"ui.construction.free_hint": "建構直線:按一下兩點;按右鍵可重新開始",
|
||||
"ui.construction.normal_needs_length": "無限長直線一律是建構直線:輸入長度即可繪製一般直線",
|
||||
"ui.construction.point_hint": "建構直線:按一下要通過的點",
|
||||
"ui.construction.reference_hint": "建構直線:選取一條直線,再選取一點;按右鍵可變更參考",
|
||||
"ui.construction.ribbon_label": "輔助線",
|
||||
"ui.construction.side_hint": "建構直線:按一下直線要延伸的那一側;按右鍵可重新開始",
|
||||
"ui.context_menu": "右鍵選單",
|
||||
"ui.continuity_crease": "摺痕",
|
||||
"ui.continuity_curvature": "曲率",
|
||||
"ui.continuity_curvature_change": "曲率變化",
|
||||
"ui.continuity_g0": "G0 摺痕",
|
||||
"ui.continuity_g1": "G1 相切",
|
||||
"ui.continuity_g2": "G2 曲率",
|
||||
"ui.continuity_gap": "間隙",
|
||||
"ui.continuity_position": "位置",
|
||||
"ui.continuity_tangent": "相切",
|
||||
"ui.copy": "複製",
|
||||
"ui.corner": "轉角",
|
||||
"ui.corners": "角點",
|
||||
@@ -4677,6 +4752,7 @@
|
||||
"ui.hideOrigin": "隱藏原點",
|
||||
"ui.hidePlanes": "隱藏平面",
|
||||
"ui.hideProfile": "隱藏輪廓",
|
||||
"ui.hide_component_history": "隱藏其歷程記錄",
|
||||
"ui.hide_origin_by_default": "預設隱藏原點",
|
||||
"ui.hide_origin_note": "新設計開啟時會隱藏「原點」資料夾。設計一旦顯示或隱藏過原點,就會保留自己的原點設定。",
|
||||
"ui.history_back": "後退一步",
|
||||
@@ -4684,6 +4760,7 @@
|
||||
"ui.history_edge_hint": "雙擊邊",
|
||||
"ui.history_first": "移至開頭",
|
||||
"ui.history_forward": "前進一步",
|
||||
"ui.history_hide_hidden": "隱藏已隱藏的元件",
|
||||
"ui.history_hide_names": "隱藏名稱",
|
||||
"ui.history_last": "移至結尾",
|
||||
"ui.history_marker_tip": "歷程標記:拖曳可回溯模型;新特徵會加在此處",
|
||||
@@ -4693,10 +4770,12 @@
|
||||
"ui.history_play_button_show": "顯示播放按鈕",
|
||||
"ui.history_play_finish_action": "播放歷程記錄前,請先完成目前的操作",
|
||||
"ui.history_play_roll_back": "播放前,請先將歷程記錄標記往回移",
|
||||
"ui.history_show_hidden": "顯示已隱藏的元件",
|
||||
"ui.history_show_names": "顯示名稱",
|
||||
"ui.history_sketch_listed_under": "列在 {feature} 之下",
|
||||
"ui.history_title": "歷程記錄",
|
||||
"ui.history_toggle_names": "顯示或隱藏特徵名稱",
|
||||
"ui.history_waits_for_editor": "復原、重做與歷程記錄會等到特徵編輯器開啟後才可使用",
|
||||
"ui.hole": "孔",
|
||||
"ui.hole_tap_type": "攻牙類型",
|
||||
"ui.hole_type": "孔類型",
|
||||
@@ -4977,6 +5056,7 @@
|
||||
"ui.showDimension": "顯示尺寸",
|
||||
"ui.showOrigin": "顯示原點",
|
||||
"ui.showPlanes": "顯示平面",
|
||||
"ui.show_component_history": "顯示其歷程記錄",
|
||||
"ui.show_constraints": "拘束條件",
|
||||
"ui.show_construction_geometries": "建構幾何",
|
||||
"ui.show_dimensions": "尺寸",
|
||||
@@ -5159,7 +5239,7 @@
|
||||
"update.check_unavailable": "此安裝版本無法檢查更新。",
|
||||
"update.checking": "正在檢查…",
|
||||
"update.copy": "複製",
|
||||
"update.dmg_opened": "磁碟映像已驗證並開啟:將 SolidCraft 拖到「應用程式」取代舊版本,然後重新啟動 SolidCraft。",
|
||||
"update.dmg_opened": "磁碟映像已驗證並開啟:結束 SolidCraft,將新的 SolidCraft 拖到「應用程式」取代舊版本,然後再次開啟 SolidCraft。",
|
||||
"update.download": "下載",
|
||||
"update.download_open": "下載並開啟",
|
||||
"update.downloading": "正在下載並驗證… {done} / {total} MB",
|
||||
@@ -5203,6 +5283,7 @@
|
||||
"web.open_link": "開啟連結的設計?",
|
||||
"web.open_link_confirm": "有未儲存的變更。要在新分頁中開啟連結的設計嗎?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ 選擇 · Enter / Tab 自動完成 · Esc 關閉",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ 選擇 · Enter 套用此值",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ 選擇 · Enter 照原樣套用 · Esc 關閉",
|
||||
"⌘Enter continues on GitHub.": "按 ⌘Enter 在 GitHub 上繼續。"
|
||||
}
|
||||
|
||||
+188
-14
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "矩形",
|
||||
"Rectangular Tube": "矩形管",
|
||||
"Reference face": "参考面",
|
||||
"Reference plane": "参考平面",
|
||||
"Relief Depth": "止裂槽深度",
|
||||
"Relief Width": "止裂槽宽度",
|
||||
"Remove Tools": "移除工具",
|
||||
@@ -494,7 +495,8 @@
|
||||
"Rename Folder": "重命名文件夹",
|
||||
"Render Stage": "渲染阶段",
|
||||
"Reopen designs": "重新打开设计",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "重新打开上次关闭 SolidCraft 时打开的设计",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "重新打开上次打开的设计",
|
||||
"Repair Part": "修复零件",
|
||||
"Replace existing library file": "替换现有库文件",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "报告缺陷 — 复核截图并开启 GitHub 草稿",
|
||||
"Report copied. Keep this text to submit on GitHub when you are online.": "报告已复制。请保留此文本,联网后在 GitHub 上提交。",
|
||||
@@ -789,6 +791,7 @@
|
||||
"browser.edit_canvas_title": "编辑画布:{name}",
|
||||
"browser.edit_point_cloud_title": "编辑点云: {name}",
|
||||
"browser.move_title": "移动:{name}",
|
||||
"browser.needs_repair": "{name}:需要修复",
|
||||
"browser.parameters": "参数 ({count})",
|
||||
"browser.precision": "精度: {precision}",
|
||||
"browser.sketch_attachment_lost": "{name} (附着丢失)",
|
||||
@@ -833,6 +836,7 @@
|
||||
"cmd.assembly_mass_properties": "质量属性",
|
||||
"cmd.body.print_face": "打印底面",
|
||||
"cmd.body_rename": "重命名实体",
|
||||
"cmd.body_repair": "修复零件",
|
||||
"cmd.browser_collapse_all": "全部折叠",
|
||||
"cmd.browser_expand_all": "全部展开",
|
||||
"cmd.browser_group": "新建组",
|
||||
@@ -917,6 +921,8 @@
|
||||
"cmd.diagnostics_crash_reports": "崩溃报告",
|
||||
"cmd.diagnostics_crash_reports_clear": "删除崩溃报告",
|
||||
"cmd.diagnostics_crash_test": "崩溃测试",
|
||||
"cmd.diagnostics_profile": "性能分析",
|
||||
"cmd.diagnostics_validation": "验证",
|
||||
"cmd.doc_recover": "恢复设计",
|
||||
"cmd.doc_recovery_discard": "丢弃已恢复的设计",
|
||||
"cmd.doc_recovery_list": "可恢复的设计",
|
||||
@@ -954,6 +960,7 @@
|
||||
"cmd.frame_profile_list": "型材库",
|
||||
"cmd.frame_profile_properties": "型材属性",
|
||||
"cmd.inspect_accessibility": "可达性分析",
|
||||
"cmd.inspect_analysis": "视图分析",
|
||||
"cmd.inspect_center_of_mass": "质心",
|
||||
"cmd.inspect_clearance": "间隙",
|
||||
"cmd.inspect_colored_components": "启用组件颜色",
|
||||
@@ -977,6 +984,7 @@
|
||||
"cmd.joint_delete": "删除接头",
|
||||
"cmd.joint_drive": "驱动接头",
|
||||
"cmd.joint_edit": "编辑接头",
|
||||
"cmd.joint_find_limits": "查找接头限制",
|
||||
"cmd.joint_gear": "齿轮",
|
||||
"cmd.joint_inspect": "检查接头",
|
||||
"cmd.joint_limits": "接头限制",
|
||||
@@ -1358,6 +1366,7 @@
|
||||
"crash.unclean_explanation": "在后续启动时检测到;未记录确切的退出时间。系统关机或强制停止也可能导致会话未正常结束。",
|
||||
"delete.and_more": "…以及另外 {count} 项",
|
||||
"diagnostic.empty_boolean_component": "组件 '{component}' 中没有可供切除或求交的实体;工具可能与组件 '{other}' 中的 '{body}' 重叠 — 请激活该组件或选择该实体作为目标",
|
||||
"diagnostic.reselect_reference": "{feature}:引用 `{reference}` 已不再是选择时的样子;请重新选择",
|
||||
"dialog.bodies_count": "{count} 个实体",
|
||||
"dialog.clearance_invalid": "间隙必须是 0 或更大的长度",
|
||||
"dialog.compute": "计算",
|
||||
@@ -1423,6 +1432,14 @@
|
||||
"file_load.reading_percent": "正在读取文件... {percent}%",
|
||||
"file_load.rebuilding": "正在重建特征 {done}/{total}",
|
||||
"file_load.recovering": "正在恢复设计",
|
||||
"file_load.step_build": "正在构建模型",
|
||||
"file_load.step_feature": "特征 {done}/{total}",
|
||||
"file_load.step_part": "零件 {done}/{total}",
|
||||
"file_load.step_percent": "{percent}%",
|
||||
"file_load.step_read": "正在读取文件",
|
||||
"file_load.step_rebuild": "正在重建特征",
|
||||
"file_load.step_repair": "正在检查和修复零件",
|
||||
"file_load.step_show": "正在准备视图",
|
||||
"file_load.stopping": "正在等待上一次操作停止...",
|
||||
"file_load.updating_links": "正在更新链接",
|
||||
"file_load.waiting": "正在等待文件操作",
|
||||
@@ -1717,6 +1734,7 @@
|
||||
"help.timeline.delete": "从时间线中删除所选特征;删除草图时,由它生成的特征也会一起删除。用于去掉不再需要的步骤。",
|
||||
"help.timeline.dependents": "列出会随某个特征一同被删除的特征,以及缺少它就会失败的特征。用于在删除或更改特征之前。",
|
||||
"help.timeline.play": "逐个播放剩余特征,每次重建后暂停半秒。再次运行此命令或按Escape停止;转到末尾会立即跳转。",
|
||||
"help.version": "版本 {version}",
|
||||
"help.view.apply": "将视图转到前视、俯视、等轴测等标准方向,或转到已保存的命名视图。用于从固定角度观察模型。",
|
||||
"help.view.delete": "删除已保存的命名视图。模型不会改变。",
|
||||
"help.view.fit": "缩放并平移,使整个模型或所选对象充满视图。用于模型跑出视野时。",
|
||||
@@ -1804,6 +1822,27 @@
|
||||
"home.start_hint": "创建设计、打开最近的文件或浏览示例。",
|
||||
"home.whats_new": "新功能",
|
||||
"home.yesterday": "昨天",
|
||||
"import_report.details": "详细信息:",
|
||||
"import_report.file_one": "文件还有 1 个其他问题。",
|
||||
"import_report.file_other": "文件还有 {count} 个其他问题。",
|
||||
"import_report.hint": "从创建这些零件的程序重新导出可能会解决此问题。“复制警告消息”中有详细信息。",
|
||||
"import_report.left_out_one": "1 个面无法修复,已缺失。",
|
||||
"import_report.left_out_other": "{count} 个面无法修复,已缺失。",
|
||||
"import_report.needs_repair_one": "1 个零件需要修复后才能建模。",
|
||||
"import_report.needs_repair_other": "{count} 个零件需要修复后才能建模。",
|
||||
"import_report.not_imported_one": "1 个零件无法导入。",
|
||||
"import_report.not_imported_other": "{count} 个零件无法导入。",
|
||||
"import_report.not_shown_one": "1 个面无法绘制,已隐藏。",
|
||||
"import_report.not_shown_other": "{count} 个面无法绘制,已隐藏。",
|
||||
"import_report.open_one": "1 个零件的面之间有缝隙,作为开放实体导入。",
|
||||
"import_report.open_other": "{count} 个零件的面之间有缝隙,作为开放实体导入。",
|
||||
"import_report.refused_one": "1 个零件无法生成实体,未导入。",
|
||||
"import_report.refused_other": "{count} 个零件无法生成实体,未导入。",
|
||||
"import_report.repaired_one": "已修复 1 个零件中的小缺陷。",
|
||||
"import_report.repaired_other": "已修复 {count} 个零件中的小缺陷。",
|
||||
"import_report.solids": "{parts} 个零件中有 {solids} 个作为实体导入。",
|
||||
"import_report.surfaces_one": "1 个零件无法生成实体,作为曲面导入。",
|
||||
"import_report.surfaces_other": "{count} 个零件无法生成实体,作为曲面导入。",
|
||||
"install.button_tip": "将 SolidCraft 安装为应用:在独立窗口中打开,离线也能使用",
|
||||
"install.dock_1": "选择“文件”›“添加到程序坞”。",
|
||||
"install.dock_2": "点按“添加”。",
|
||||
@@ -1828,7 +1867,18 @@
|
||||
"issue.note_save_failed": "无法保存截图:{error}",
|
||||
"issue.note_saved": "截图已保存。请在 GitHub 草稿中附上 PNG。",
|
||||
"issue.save_screenshot": "保存截图…",
|
||||
"joint.find_limits": "查找限制",
|
||||
"joint.find_limits_failed": "查找限制时出错停止",
|
||||
"joint.find_limits_hint": "根据此接头移动的零件首次接触其他零件的位置填写限制,从当前位置向两个方向旋转或滑动。在副本上进行:按“确定”前不会更改任何内容。",
|
||||
"joint.find_limits_stale": "找到这些限制后接头或设计已更改:请重新查找",
|
||||
"joint.finding_limits": "正在查找限制…",
|
||||
"joint.from_bodies_hint": "每个所选实体移入各自的新组件,位置保持不变。",
|
||||
"joint.limit_contact": "{side} {value}:{a} 接触 {b}",
|
||||
"joint.limit_limit": "{side} {value}:另一个值或接头的限制",
|
||||
"joint.limit_loop": "{side} {value}:超过此处闭环无法闭合",
|
||||
"joint.limit_range": "{side}:{range} 内无接触,保持不变",
|
||||
"joint.limit_turn": "{side} {value}:一圈内无接触",
|
||||
"joint.limits_overlapping": "此处已相互重叠:{pairs}。只有更深入时才会停止查找。",
|
||||
"joint.one_body": "两次选择都在 {body} 上。请在另一个零件上选择第二个。",
|
||||
"joint.one_component": "这些零件是同一组件的实体:{bodies}。接头使一个组件相对另一个组件运动,因此每个零件都需要自己的组件。",
|
||||
"joint.rigid_group_one_component": "这 {count} 个实体都位于同一组件中(或直接位于设计中):请先使用“{command}”。",
|
||||
@@ -2091,12 +2141,19 @@
|
||||
"parts.family.button_head_cap_screw": "圆头内六角螺钉",
|
||||
"parts.family.dowel_pin": "圆柱销",
|
||||
"parts.family.hex_bolt": "六角头螺栓",
|
||||
"parts.family.hex_cap_screw_inch": "六角头螺栓(英制)",
|
||||
"parts.family.hex_nut": "六角螺母",
|
||||
"parts.family.hex_nut_inch": "六角螺母(英制)",
|
||||
"parts.family.set_screw": "内六角平端紧定螺钉",
|
||||
"parts.family.socket_head_cap_screw": "内六角圆柱头螺钉",
|
||||
"parts.family.washer": "平垫圈",
|
||||
"parts.family.washer_sae": "平垫圈,SAE(窄型)",
|
||||
"parts.family.washer_uss": "平垫圈,USS(宽型)",
|
||||
"parts.holes": "孔(可选)",
|
||||
"parts.placement_hint": "选择一个或多个孔,在每个孔中各装一个零件;或按确定将其放在原点",
|
||||
"parts.system": "单位制",
|
||||
"parts.system.inch": "英制",
|
||||
"parts.system.metric": "公制",
|
||||
"pattern.alignment_2": "对齐 2",
|
||||
"pattern.alignment_hint": "数量包含原始图形。居中时副本分布在两侧;数量为偶数时,反转可切换多一个副本的一侧。",
|
||||
"pattern.direction_1": "方向 1",
|
||||
@@ -2135,6 +2192,18 @@
|
||||
"prefs.alt_frees_manipulators": "Alt / Option 也可自由拖动操纵器箭头",
|
||||
"prefs.anti_aliasing_amd_dx12": "由于启动检查在启用多重采样时未绘制任何内容,而在1×时通过了检查,抗锯齿已设为1×。您可以在上方选择其他设置;重启后若检查通过,该设置将生效。",
|
||||
"prefs.anti_aliasing_note": "平滑锯齿边缘。",
|
||||
"prefs.appearance.colour_vision_help": "更改具有含义的颜色:草图状态、坐标轴、折弯、分析和图标。",
|
||||
"prefs.appearance.desktop_only": "仅限桌面",
|
||||
"prefs.appearance.follow_help": "其他工具重写文件时重新加载。",
|
||||
"prefs.appearance.interface_scale": "界面缩放",
|
||||
"prefs.appearance.next_themes": "下一个主题",
|
||||
"prefs.appearance.option_count": "{count} 个选项",
|
||||
"prefs.appearance.option_count_one": "{count} 个选项",
|
||||
"prefs.appearance.previous_themes": "上一个主题",
|
||||
"prefs.appearance.theme_count": "{count} 个主题",
|
||||
"prefs.appearance.theme_own": "主题自身",
|
||||
"prefs.appearance.viewport_colours": "3D 视图颜色",
|
||||
"prefs.appearance.viewport_logo": "视口和徽标",
|
||||
"prefs.applies_on_restart": "重启后生效",
|
||||
"prefs.assistant": "助手",
|
||||
"prefs.auto_operation": "自动选择操作",
|
||||
@@ -2149,6 +2218,8 @@
|
||||
"prefs.bottom_centre": "底部居中",
|
||||
"prefs.bottom_right": "右下角",
|
||||
"prefs.browser": "浏览器",
|
||||
"prefs.browser_components_open": "组件初始展开",
|
||||
"prefs.browser_components_open_note": "浏览器中的每个组件都像以前一样初始展开,而不是在您展开前保持折叠。",
|
||||
"prefs.browser_eye_left": "显示切换位于折叠箭头旁",
|
||||
"prefs.browser_eye_left_note": "浏览器各行的显示切换像以前一样位于折叠箭头旁,而不是在行右端的一列中。",
|
||||
"prefs.browser_reveal_on_hover": "悬停时显示",
|
||||
@@ -2198,7 +2269,19 @@
|
||||
"prefs.font_sample": "字体示例 · Ø12.50 mm · 45.0° · 0123456789",
|
||||
"prefs.font_unreadable": "无法读取所选字体。",
|
||||
"prefs.general": "常规",
|
||||
"prefs.general_note": "语言、保存和更新。",
|
||||
"prefs.general.active": "当前",
|
||||
"prefs.general.crash_reports": "崩溃报告",
|
||||
"prefs.general.edited": "已修改",
|
||||
"prefs.general.held": "包含",
|
||||
"prefs.general.import": "导入...",
|
||||
"prefs.general.last_check": "上次检查:{time}",
|
||||
"prefs.general.minutes": "分钟",
|
||||
"prefs.general.name_setup": "为此配置命名",
|
||||
"prefs.general.profile_name": "配置文件名称",
|
||||
"prefs.general.save_new": "另存为新配置",
|
||||
"prefs.general.step_import": "STEP 和导入",
|
||||
"prefs.general.this_computer": "此计算机",
|
||||
"prefs.general_note": "配置文件、语言、文件和更新。",
|
||||
"prefs.gesture_orbit": "环绕",
|
||||
"prefs.gesture_pan": "平移",
|
||||
"prefs.gesture_zoom": "缩放",
|
||||
@@ -2255,10 +2338,10 @@
|
||||
"prefs.logo_accent": "强调色",
|
||||
"prefs.logo_theme": "主题颜色",
|
||||
"prefs.max_file_size": "可打开的最大文件",
|
||||
"prefs.max_file_size_design": "设计文件",
|
||||
"prefs.max_file_size_design": "可打开的最大文件:设计",
|
||||
"prefs.max_file_size_flag": "本次启动指定了 --max-file-size {size},该值在关闭 SolidCraft 前有效。不带此选项启动时才会应用这些设置。",
|
||||
"prefs.max_file_size_note": "默认值:512 MB 和 256 MB;上限为 16 GB。打开文件需要数倍于文件大小的内存。保留大型导入数据的设计可能需要提高设计文件上限,才能保存并重新打开。",
|
||||
"prefs.max_file_size_step": "STEP 和导入文件",
|
||||
"prefs.max_file_size_step": "可打开的最大文件:STEP 和导入",
|
||||
"prefs.max_file_size_web": "网页版可打开最大 512 MB 的 STEP 和导入文件,以及最大 256 MB 的设计:浏览器标签页无法容纳更大的文件。桌面版可提高这些上限。",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "大图标",
|
||||
@@ -2275,7 +2358,7 @@
|
||||
"prefs.navigator_fit_note": "点击立方体的面、角或箭头,或坐标轴指示器的轴时,也会从各个方向都能容纳模型的同一距离(其包围球)注视模型中心。开启期间主视图使用相同的缩放和中心,因此之后选择的视图只改变方向。关闭:视图原地旋转,保持目标点和缩放。",
|
||||
"prefs.network_host_remove": "删除",
|
||||
"prefs.network_locations": "允许设计中的网络位置",
|
||||
"prefs.network_locations_note": "关闭:设计中位于其他计算机 (\\\\server\\share) 上的链接文件,只会从此处列出的服务器打开,或在询问时你允许的服务器打开。",
|
||||
"prefs.network_locations_note": "适用于将设计存放在文件服务器上的组织。关闭时:仅允许您授权的服务器。",
|
||||
"prefs.network_locations_section": "网络位置",
|
||||
"prefs.not_on_gpu": "此 GPU 不支持",
|
||||
"prefs.on": "开启",
|
||||
@@ -2301,10 +2384,12 @@
|
||||
"prefs.profile_applied": "已应用配置文件。",
|
||||
"prefs.profile_changed": "保存配置文件后设置已更改。“保存”会将其写入配置文件。",
|
||||
"prefs.profile_deleted": "已删除配置文件。",
|
||||
"prefs.profile_export": "导出…",
|
||||
"prefs.profile_export": "导出",
|
||||
"prefs.profile_exported": "已导出配置文件。",
|
||||
"prefs.profile_imported": "已导入配置文件。在“当前配置文件”中选择它即可切换。",
|
||||
"prefs.profile_imported": "已导入配置文件。",
|
||||
"prefs.profile_name_hint": "配置文件名称,例如 笔记本",
|
||||
"prefs.profile_operations": "分析操作耗时",
|
||||
"prefs.profile_operations_note": "记录 SMLib 调用、内核操作、特征、命令和导入所用的时间。智能体通过 diagnostics.profile 读取。",
|
||||
"prefs.profile_rename": "重命名",
|
||||
"prefs.profile_renamed": "已重命名配置文件。",
|
||||
"prefs.profile_save": "保存",
|
||||
@@ -2323,16 +2408,20 @@
|
||||
"prefs.render_previous_geometry_budget": "使用先前的渲染预览几何内存预算",
|
||||
"prefs.render_previous_geometry_budget_help": "恢复先前的固定几何内存上限(64位桌面为512 MiB,其他平台为128 MiB),替代基于设备的预算。",
|
||||
"prefs.reports_saved": "已保存 {n} 份",
|
||||
"prefs.restore_page_defaults": "恢复本页默认值",
|
||||
"prefs.ribbon_click_ends_tool": "单击正在运行的工具的按钮将结束该工具",
|
||||
"prefs.ribbon_click_ends_tool_note": "单击正在使用的草图工具的高亮功能区按钮会结束该工具,在视图中单击即可再次选择。关闭:单击会重新启动该工具。",
|
||||
"prefs.ribbon_reveal": "在功能区中显示搜索结果",
|
||||
"prefs.ribbon_reveal_note": "将指针悬停在“搜索命令”的结果上时,会显示该命令在功能区中的位置:其选项卡、按钮或面板下拉菜单中的行。从搜索启动的命令会在那里闪烁,从下拉菜单运行的命令会标记其面板。",
|
||||
"prefs.right_click": "右键单击",
|
||||
"prefs.saving": "自动保存和恢复",
|
||||
"prefs.saving": "启动和保存",
|
||||
"prefs.scale_note": "也可在窗口中使用 Ctrl + / Ctrl −。100% 时按钮高度为 28 像素。",
|
||||
"prefs.scale_text": "缩放与文字",
|
||||
"prefs.search": "搜索设置",
|
||||
"prefs.search_empty": "没有匹配的设置",
|
||||
"prefs.section_hatch": "剖面线",
|
||||
"prefs.section_hatch_by_part": "按零件",
|
||||
"prefs.section_hatch_one": "单一样式",
|
||||
"prefs.select": "选择",
|
||||
"prefs.select_one_item": "选择一个项目",
|
||||
"prefs.select_other_hidden": "使用“选择其他”选择隐藏几何体",
|
||||
@@ -2447,6 +2536,12 @@
|
||||
"prefs.under_cursor": "光标下方",
|
||||
"prefs.units_precision": "单位与精度",
|
||||
"prefs.updates": "更新",
|
||||
"prefs.validation": "实体检查",
|
||||
"prefs.validation.each_feature": "每个特征一次",
|
||||
"prefs.validation.each_step": "每一步之后",
|
||||
"prefs.validation.label": "内核完整检查",
|
||||
"prefs.validation.off": "关闭(实验性)",
|
||||
"prefs.validation_note": "每一步之后:检查每次扫掠、布尔运算和倒圆。\n每个特征一次:每个特征的结果检查一次。\n关闭(实验性):不检查。",
|
||||
"prefs.value_boxes_in_view": "在视图中于手柄旁显示数值",
|
||||
"prefs.value_boxes_in_view_tip": "命令的数值(距离、半径等)显示在模型上方手柄旁的框中。关闭后,在命令窗口中输入数值,选取后键盘输入会进入该窗口,模型不会被遮挡。",
|
||||
"prefs.view": "视图行为",
|
||||
@@ -2481,6 +2576,17 @@
|
||||
"recovery.just_now": "刚刚",
|
||||
"recovery.min_ago": "{count} 分钟前",
|
||||
"recovery.never_saved": "从未保存",
|
||||
"refused.change": "无法更改 {body}。",
|
||||
"refused.cut": "无法切割 {body}。",
|
||||
"refused.face_curved": "它的一个曲面无法用于建模。",
|
||||
"refused.face_flat": "它的一个平面无法用于建模。",
|
||||
"refused.face_freeform": "它的一个自由曲面无法用于建模。",
|
||||
"refused.face_rounded": "它的一个圆角面无法用于建模。",
|
||||
"refused.faces_apart": "它的面贴合得不够紧密,无法用于建模。",
|
||||
"refused.intersect": "无法与 {body} 求交。",
|
||||
"refused.join": "无法合并到 {body}。",
|
||||
"refused.repair": "修复零件",
|
||||
"refused.show_face": "显示面",
|
||||
"render.adaptive_absolute": "RGB 绝对误差下限",
|
||||
"render.adaptive_coverage": "覆盖率误差目标",
|
||||
"render.adaptive_enable": "使用独立预采样分配样本",
|
||||
@@ -3948,11 +4054,20 @@
|
||||
"the section's profiles must be in one sketch": "截面的轮廓必须位于同一个草图中",
|
||||
"the two origins are on the same component": "这两个原点位于同一个组件上",
|
||||
"thread.automatic_size": "自动尺寸",
|
||||
"thread.chamfer_depth_needed": "此牙型必填",
|
||||
"thread.designation": "螺纹标记",
|
||||
"thread.direction": "螺纹方向",
|
||||
"thread.drill_range": "丝锥制造商的钻头为 {drill} mm。内螺纹的标准小径为 {minor} mm。可使用 {low} 至 {high} mm 的钻头。",
|
||||
"thread.drill_range_custom": "钻头为大径减螺距,{drill} mm。内螺纹的标准小径为 {minor} mm。可使用 {low} 至 {high} mm 的钻头。",
|
||||
"thread.drill_range_taper": "丝锥制造商的钻头为 {drill} mm。螺纹终端处的标准小径为 {minor} mm。可使用 {low} 至 {high} mm 的钻头。",
|
||||
"thread.end_square": "直角",
|
||||
"thread.fade_short": "1.25 P(ISO 3508)",
|
||||
"thread.fade_standard": "2.5 P(ISO 3508)",
|
||||
"thread.fits_the_face": "适合该面,例如 {example}",
|
||||
"thread.form_bspp": "管螺纹 G(ISO 228-1)",
|
||||
"thread.form_bspt": "圆锥管螺纹 R、Rc(ISO 7-1)",
|
||||
"thread.form_npt": "圆锥管螺纹 NPT(ASME B1.20.1)",
|
||||
"thread.form_rp": "圆柱内管螺纹 Rp(ISO 7-1)",
|
||||
"thread.iso_metric": "ISO 公制",
|
||||
"thread.left_hand": "左旋",
|
||||
"thread.right_hand": "右旋",
|
||||
"thread.select_size": "选择尺寸",
|
||||
"timeline.delete_after_marker_one": "删除历史标记之后的 1 个特征",
|
||||
"timeline.delete_after_marker_other": "删除历史标记之后的 {count} 个特征",
|
||||
@@ -4126,6 +4241,7 @@
|
||||
"ui.agent_chat_auth_terminal": "在终端中运行以下命令,然后再次点击连接:",
|
||||
"ui.agent_chat_auth_title": "登录智能体",
|
||||
"ui.agent_chat_cancel": "取消",
|
||||
"ui.agent_chat_cancel_timeout": "代理未响应停止请求,已断开连接。请重新连接以继续。",
|
||||
"ui.agent_chat_cannot_start": "已安装,但无法启动:{error}",
|
||||
"ui.agent_chat_changes": "{n} 项更改,一步撤销",
|
||||
"ui.agent_chat_changes_n": "{n} 项更改",
|
||||
@@ -4203,6 +4319,7 @@
|
||||
"ui.agent_chat_no_images": "该智能体不接收图片",
|
||||
"ui.agent_chat_no_picture": "剪贴板上没有图片",
|
||||
"ui.agent_chat_no_tool_call": "模型没有使用 SolidCraft 的工具,因此设计没有更改。请选择支持工具调用的模型;参见智能体聊天指南中的“本地模型”。",
|
||||
"ui.agent_chat_node_too_old": "Node.js {version} 版本过旧,无法运行此代理。请安装 Node.js 20.10 或更新版本,然后点击刷新。",
|
||||
"ui.agent_chat_none_found": "未找到编程智能体。请安装以下之一,然后点击刷新:",
|
||||
"ui.agent_chat_none_found_short": "未找到智能体",
|
||||
"ui.agent_chat_not_installed": "未安装",
|
||||
@@ -4422,6 +4539,8 @@
|
||||
"ui.centerline": "中心线",
|
||||
"ui.chain": "链",
|
||||
"ui.chamfer": "倒角",
|
||||
"ui.chamfer_angle": "倒角角度",
|
||||
"ui.chamfer_depth": "倒角深度",
|
||||
"ui.chamfer_type": "倒角类型",
|
||||
"ui.change_workspace": "更改工作区",
|
||||
"ui.circle": "圆",
|
||||
@@ -4452,10 +4571,16 @@
|
||||
"ui.colour_vision_sample": "示例",
|
||||
"ui.colour_vision_tritan": "蓝色觉异常(Tritan)",
|
||||
"ui.colour_vision_tritan_tip": "蓝色感知较弱或缺失时使用",
|
||||
"ui.comb_length": "梳长",
|
||||
"ui.comb_length_tip": "梳齿的长度:自动长度的倍数。自动长度使最长的梳齿为所示曲线尺寸的五分之一。",
|
||||
"ui.comb_teeth": "梳齿数",
|
||||
"ui.comb_teeth_tip": "每个梳的梳齿数,沿曲线均匀分布。梳齿越多,越能看清曲率如何变化。",
|
||||
"ui.combine": "组合",
|
||||
"ui.coming_later": "即将推出",
|
||||
"ui.command_done": "{command} 完成",
|
||||
"ui.command_palette_hint": "输入命令,例如 {example} — 或按 S 搜索",
|
||||
"ui.components_from_bodies_hint": "在活动组件下,为每个选定实体创建一个以该实体命名的组件。",
|
||||
"ui.components_from_bodies_required": "请至少选择一个实体。",
|
||||
"ui.concentric": "同心",
|
||||
"ui.cone": "圆锥体",
|
||||
"ui.conic_curve": "圆锥曲线",
|
||||
@@ -4474,11 +4599,24 @@
|
||||
"ui.construction.ribbon_label": "辅助线",
|
||||
"ui.construction.side_hint": "构造直线:单击直线延伸的一侧;右键单击重新开始",
|
||||
"ui.context_menu": "上下文菜单",
|
||||
"ui.continuity_crease": "折痕",
|
||||
"ui.continuity_curvature": "曲率",
|
||||
"ui.continuity_curvature_change": "曲率变化",
|
||||
"ui.continuity_curvature_change_tip": "曲率变化 (G3):曲率在接缝处的变化也是平滑的。",
|
||||
"ui.continuity_curvature_tip": "曲率连续 (G2):反射平滑穿过,没有折痕。",
|
||||
"ui.continuity_g0": "G0 折痕",
|
||||
"ui.continuity_g1": "G1 相切",
|
||||
"ui.continuity_g2": "G2 曲率",
|
||||
"ui.continuity_gap": "缝隙",
|
||||
"ui.continuity_position": "位置",
|
||||
"ui.continuity_position_tip": "位置 (G0):曲面相接,但接缝处有折痕。",
|
||||
"ui.continuity_smooth_curvature_change": "平滑曲率变化",
|
||||
"ui.continuity_smooth_curvature_change_tip": "平滑曲率变化 (G4):连曲率的变化方式在接缝处也平滑延续。",
|
||||
"ui.continuity_tangent": "相切",
|
||||
"ui.continuity_tangent_tip": "相切 (G1):没有折痕,但曲率在接缝处突变。",
|
||||
"ui.copy": "复制",
|
||||
"ui.copy_details": "复制详细信息",
|
||||
"ui.copy_details_tip": "复制完整消息,以便查找或报告。",
|
||||
"ui.corner": "角",
|
||||
"ui.corners": "角点",
|
||||
"ui.cosmetic_thread_markings": "装饰螺纹标记",
|
||||
@@ -4596,8 +4734,10 @@
|
||||
"ui.edit_sketch_empty": "没有可编辑的草图",
|
||||
"ui.ellipse": "椭圆",
|
||||
"ui.emboss_wrap_note": "与受支持圆柱相切的正交线轮廓会自动缠绕。选择一个壁以指定目标。",
|
||||
"ui.end": "末端",
|
||||
"ui.end_radius": "结束半径",
|
||||
"ui.end_scale": "末端缩放",
|
||||
"ui.ends": "端部",
|
||||
"ui.entities": "实体",
|
||||
"ui.equal": "相等",
|
||||
"ui.equation_end": "结束",
|
||||
@@ -4617,6 +4757,7 @@
|
||||
"ui.export_ellipsis_keymap": "导出…",
|
||||
"ui.export_keymap_tip": "将按键、S 框收藏和工具栏导出为文件",
|
||||
"ui.extend": "延伸",
|
||||
"ui.extent": "范围",
|
||||
"ui.extent_type": "延伸类型",
|
||||
"ui.extract_component": "提取到文件…",
|
||||
"ui.extract_design_changed": "选择文件时设计已更改。请重新开始提取到文件。",
|
||||
@@ -4625,11 +4766,39 @@
|
||||
"ui.extrude_auto_to_object": "拉伸到所选目标",
|
||||
"ui.extrude_auto_to_object_tip": "在拉伸中选择轮廓后,选取其平面外的面、顶点或平行平面会将延伸类型切换为“到对象”",
|
||||
"ui.factor": "系数",
|
||||
"ui.fade": "收尾",
|
||||
"ui.fade_length": "收尾长度",
|
||||
"ui.feature_history": "特征历史",
|
||||
"ui.feature_options": "特征选项",
|
||||
"ui.features": "特征",
|
||||
"ui.file": "文件",
|
||||
"ui.fillet": "圆角",
|
||||
"ui.fillet_corner_ball": "球面",
|
||||
"ui.fillet_corner_ball_tip": "滚球球面的一部分,与每个圆角相切 (G1)。",
|
||||
"ui.fillet_corner_curvature_tip": "曲率 (G2):平滑曲面片。角点沿每条边后退一个后退距离(未输入时为半径的 {factor} 倍),并用曲面片填充,反射穿过时没有折痕。",
|
||||
"ui.fillet_corner_needs_constant": "平滑曲面片角点 (G2) 需要恒定半径和精确的半径拟合。",
|
||||
"ui.fillet_corner_patch": "平滑曲面片",
|
||||
"ui.fillet_corner_patch_tip": "曲率连续 (G2):角点沿每条边后退,并用曲面片填充,反射穿过时没有折痕。",
|
||||
"ui.fillet_corner_tangent_tip": "相切 (G1):滚球角,滚球球面的一部分,与每个圆角相切。",
|
||||
"ui.fillet_error_corner": "无法在此处构建平滑角点。请尝试其他后退距离,或相切角点。",
|
||||
"ui.fillet_error_failed": "无法在这些边上构建此圆角。请尝试其他半径,或一次少倒几条边。",
|
||||
"ui.fillet_error_setback_short": "对于此角点的面片,后退距离太短:从 {least} 起可以构建。请尝试更长的后退距离。",
|
||||
"ui.fillet_error_too_large": "半径对这些边旁边的面来说太大。请尝试更小的半径,或将“面边界”设为“最大半径”。",
|
||||
"ui.fillet_fit": "面边界",
|
||||
"ui.fillet_fit_exact": "精确半径",
|
||||
"ui.fillet_fit_needs_constant": "半径拟合需要恒定半径。",
|
||||
"ui.fillet_fit_reduce": "最大半径",
|
||||
"ui.fillet_fit_taper": "收窄半径",
|
||||
"ui.fillet_fit_tip": "当半径大于边旁边的面所允许的大小时。\n精确半径:按给定的半径倒圆,否则不倒圆并说明原因。\n最大半径:用能容纳的最大半径倒每条边。\n收窄半径:保持半径,只在面不够的地方收窄圆角。",
|
||||
"ui.fillet_options_tip": "相切链:同时倒圆与所选边平滑相连的边。\n预览:编辑时在视图中显示结果。",
|
||||
"ui.fillet_setback": "后退距离",
|
||||
"ui.fillet_setback_hint": "{factor} × 半径",
|
||||
"ui.fillet_setback_least": "须大于 {least}",
|
||||
"ui.fillet_setback_tip": "平滑角点曲面片开始的位置:从角点沿每条边的距离。默认值为半径的 {factor} 倍。必须大于最小后退距离(不后退时圆角相交之处;面相互垂直时即半径)。",
|
||||
"ui.fillet_smooth_radius": "半径平滑变化",
|
||||
"ui.fillet_type_choices_tip": "恒定半径:整条边一个半径。\n弦长:无论面以什么角度相交,圆角宽度都相同。\n可变半径:两端各一个半径,添加后沿边还有更多。\n曲率连续:与平面相接处反射没有折痕的截面 (G2)。\n样条:沿边的半径跟随您设置的控制半径。",
|
||||
"ui.fillet_type_curvature": "曲率连续",
|
||||
"ui.fillet_type_tip": "横跨边的截面。曲率连续:平面之间的曲率连续 (G2) 截面,反射从面平滑过渡到圆角,没有折痕。",
|
||||
"ui.filter_commands_or_keys": "筛选命令或按键",
|
||||
"ui.findInTimeline": "在时间线中查找",
|
||||
"ui.findInWindow": "在窗口中查找",
|
||||
@@ -4761,6 +4930,7 @@
|
||||
"ui.history_sketch_listed_under": "列在 {feature} 下",
|
||||
"ui.history_title": "历史",
|
||||
"ui.history_toggle_names": "显示或隐藏特征名称",
|
||||
"ui.history_waits_for_editor": "撤销、重做和历史将等待特征编辑器打开",
|
||||
"ui.hole": "孔",
|
||||
"ui.hole_tap_type": "孔攻丝类型",
|
||||
"ui.hole_type": "孔类型",
|
||||
@@ -4778,6 +4948,7 @@
|
||||
"ui.inspect": "检查",
|
||||
"ui.instances": "实例数",
|
||||
"ui.intersect": "相交",
|
||||
"ui.iso_4753": "ISO 4753",
|
||||
"ui.isolate": "隔离",
|
||||
"ui.join": "连接",
|
||||
"ui.joint_play": "▶ 播放",
|
||||
@@ -4880,6 +5051,7 @@
|
||||
"ui.part_size_needs_a_part": "请选择已插入的标准零件(螺钉、螺母、垫圈或销)以更改其尺寸。要放大或缩小实体,请使用缩放。",
|
||||
"ui.part_size_shared": "重新生成零件并替换额外添加的特征。此零部件的所有实例都会更新。",
|
||||
"ui.paste": "粘贴",
|
||||
"ui.patch_continuity_corner_tip": "补面沿每条边以所选的连续性与周围的面相接。在周围的面彼此以不同曲率相接的角上,补面在该处保持相切 (G1)。",
|
||||
"ui.path": "路径",
|
||||
"ui.pattern_object_type_tip": "实体:在视图或浏览器中选取的实心形状,每个都会被整体复制。\n特征:历史中的步骤,例如拉伸或孔,在历史中或在其生成的面上选取;该步骤会被重复。\n草图两者都不是:请用草图自身的阵列或镜像重复其曲线,或先将其拉伸。",
|
||||
"ui.pattern_on_path": "路径阵列",
|
||||
@@ -5056,6 +5228,8 @@
|
||||
"ui.sketch_intersect_geometry": "几何体",
|
||||
"ui.sketch_intersect_projection_link": "投影链接",
|
||||
"ui.sketch_intersect_selection_filter": "选择过滤器",
|
||||
"ui.sketch_keys_only": "草图中不使用模型快捷键",
|
||||
"ui.sketch_keys_only_tip": "编辑草图时,只有草图快捷键和在任何地方都可用的快捷键会生效;E 或 F 等模型快捷键不起作用,因此误按的按键不会关闭复杂的草图。",
|
||||
"ui.sketch_modifiers": "草图修饰键",
|
||||
"ui.sketch_project_geometry": "几何体",
|
||||
"ui.sketch_report.all_constrained": "全部已完全约束。",
|
||||
@@ -5073,7 +5247,7 @@
|
||||
"ui.slicer_no_response": "{slicer} 未在规定时间内响应。文件已保存到 {path}:切片软件启动后请再次发送。",
|
||||
"ui.slicer_resend_hint": "再次发送会替换切片软件中已打开的文件。",
|
||||
"ui.slicer_single_instance": "在已运行的切片软件中打开文件",
|
||||
"ui.slicer_single_instance_tip": "PrusaSlicer、SuperSlicer 和 Cura 会在已打开的窗口中加载文件,而不是再启动一个。其他切片软件按原样启动。PrusaSlicer 3.0 始终如此:其启动器无法以其他方式接收文件。",
|
||||
"ui.slicer_single_instance_tip": "适用于将文件加载到正在运行的窗口中的切片软件。",
|
||||
"ui.slicer_starting": "正在启动 {slicer}…",
|
||||
"ui.slicer_still_starting": "仍在等待 {slicer} 启动。没有发送其他内容。",
|
||||
"ui.slicer_stl_fallback": "无法写入 3MF,因此发送的是 STL。",
|
||||
@@ -5218,13 +5392,13 @@
|
||||
"update.available": "SolidCraft {version} 已发布",
|
||||
"update.button": "更新",
|
||||
"update.button_version": "更新到 {version}",
|
||||
"update.check_automatically": "自动检查更新",
|
||||
"update.check_automatically": "启动时及每天检查一次",
|
||||
"update.check_for_updates": "检查更新",
|
||||
"update.check_now": "立即检查",
|
||||
"update.check_unavailable": "此安装版本无法检查更新。",
|
||||
"update.checking": "正在检查…",
|
||||
"update.copy": "复制",
|
||||
"update.dmg_opened": "磁盘映像已校验并打开:将 SolidCraft 拖到“应用程序”替换旧版本,然后重启 SolidCraft。",
|
||||
"update.dmg_opened": "磁盘映像已校验并打开:退出 SolidCraft,将新的 SolidCraft 拖到“应用程序”替换旧版本,然后重新打开 SolidCraft。",
|
||||
"update.download": "下载",
|
||||
"update.download_open": "下载并打开",
|
||||
"update.downloading": "正在下载并校验… {done} / {total} MB",
|
||||
|
||||
+163
-58
@@ -18,6 +18,8 @@ struct U {
|
||||
depth: vec4<f32>, // x = 1: reversed depth (1 near, 0 far); y, z: the faces' push (`FACE_BIAS`)
|
||||
tint: vec4<f32>, // xyz added to a whole hovered body's colour before lighting
|
||||
sel: vec4<f32>, // xyz a whole selected body's colour before lighting
|
||||
// the section hatch colours by part (`solidcraft_render::hatch`), the first the cap's own
|
||||
hatch: array<vec4<f32>, 3>,
|
||||
product_lights_meta: vec4<f32>, // explicit preview flag and bounded count
|
||||
product_lights: array<vec4<f32>,84>, // position/kind, radiance, axis/cone, attenuation
|
||||
sky: vec4<f32>, // xyz = world up; w = 1: shade in linear light with a sky fill (#7974)
|
||||
@@ -35,21 +37,31 @@ fn near_z(c: vec4<f32>) -> f32 {
|
||||
|
||||
@group(1) @binding(2) var acc_t: texture_2d<f32>;
|
||||
@group(2) @binding(0) var ao_depth: texture_2d<u32>;
|
||||
@group(2) @binding(1) var ao_map: texture_2d<f32>;
|
||||
|
||||
fn clipped(p: vec3<f32>) -> bool {
|
||||
return length(u.clip.xyz) > 0.5 && dot(u.clip.xyz, p) > u.clip.w;
|
||||
}
|
||||
@group(0) @binding(0) var<uniform> u: U;
|
||||
// The scene's colours, 2048 entries to a row: an R32Uint texture rather than a storage buffer,
|
||||
// so the viewport draws on WebGL2 (which has no storage buffers) as well.
|
||||
// The scene's colours, 2048 entries to a row: an Rg32Uint texture rather than a storage buffer,
|
||||
// so the viewport draws on WebGL2 (which has no storage buffers) as well. Each entry is the
|
||||
// colour's bytes, then 1 + the section hatch of the body it colours (0: the cap's own).
|
||||
@group(0) @binding(1) var pal: texture_2d<u32>;
|
||||
|
||||
/// The colour at palette entry `i`, as byte/255 (the old Unorm8x4 conversion, bit for bit).
|
||||
fn palette(i: u32) -> vec4<f32> {
|
||||
let v = textureLoad(pal, vec2<i32>(i32(i & 2047u), i32(i >> 11u)), 0).x;
|
||||
fn palette_entry(i: u32) -> vec2<u32> {
|
||||
return textureLoad(pal, vec2<i32>(i32(i & 2047u), i32(i >> 11u)), 0).xy;
|
||||
}
|
||||
|
||||
/// A palette entry's colour bytes as byte/255 (the old Unorm8x4 conversion, bit for bit).
|
||||
fn unpack_color(v: u32) -> vec4<f32> {
|
||||
return vec4<f32>(f32(v & 255u), f32((v >> 8u) & 255u), f32((v >> 16u) & 255u), f32((v >> 24u) & 255u)) / 255.0;
|
||||
}
|
||||
|
||||
/// The colour at palette entry `i`.
|
||||
fn palette(i: u32) -> vec4<f32> {
|
||||
return unpack_color(palette_entry(i).x);
|
||||
}
|
||||
|
||||
fn to_linear(c: vec3<f32>) -> vec3<f32> {
|
||||
return select(c, pow((c + vec3<f32>(0.055)) / vec3<f32>(1.055), vec3<f32>(2.4)), c > vec3<f32>(0.04045));
|
||||
}
|
||||
@@ -62,10 +74,12 @@ fn out_color(c: vec4<f32>) -> vec4<f32> {
|
||||
}
|
||||
|
||||
struct TOut {
|
||||
@builtin(position) pos: vec4<f32>,
|
||||
@invariant @builtin(position) pos: vec4<f32>,
|
||||
@location(0) n: vec3<f32>,
|
||||
@location(1) c: vec4<f32>,
|
||||
@location(2) wp: vec3<f32>,
|
||||
// 1 + the section hatch of the body (0: the cap's own), from the palette.
|
||||
@location(3) @interpolate(flat) h: u32,
|
||||
};
|
||||
|
||||
@vertex
|
||||
@@ -73,7 +87,9 @@ fn vs_tri(@location(0) p: vec3<f32>, @location(1) n: vec3<f32>, @location(2) c:
|
||||
var o: TOut;
|
||||
o.pos = u.vp * vec4<f32>(p, 1.0);
|
||||
o.n = n;
|
||||
o.c = palette(c);
|
||||
let e = palette_entry(c);
|
||||
o.c = unpack_color(e.x);
|
||||
o.h = e.y;
|
||||
o.wp = p;
|
||||
return o;
|
||||
}
|
||||
@@ -95,7 +111,9 @@ fn vs_tri_inst(
|
||||
var o: TOut;
|
||||
o.pos = u.vp * vec4<f32>(wp, 1.0);
|
||||
o.n = vec3<f32>(dot(r0.xyz, n), dot(r1.xyz, n), dot(r2.xyz, n));
|
||||
o.c = palette(c);
|
||||
let e = palette_entry(c);
|
||||
o.c = unpack_color(e.x);
|
||||
o.h = e.y;
|
||||
o.wp = wp;
|
||||
return o;
|
||||
}
|
||||
@@ -107,9 +125,10 @@ fn vs_tri_inst(
|
||||
/// stores every vertex's outputs: more of them cost more than the triangles' shared vertices
|
||||
/// save).
|
||||
struct FOut {
|
||||
@builtin(position) pos: vec4<f32>,
|
||||
@invariant @builtin(position) pos: vec4<f32>,
|
||||
@location(0) rp: vec3<f32>,
|
||||
@location(1) c: vec4<f32>,
|
||||
@location(2) @interpolate(flat) h: u32,
|
||||
};
|
||||
|
||||
@vertex
|
||||
@@ -125,7 +144,9 @@ fn vs_tri_inst_flat(
|
||||
var o: FOut;
|
||||
o.pos = u.vp * vec4<f32>(wp, 1.0);
|
||||
o.rp = wp - u.eye.xyz;
|
||||
o.c = palette(c);
|
||||
let e = palette_entry(c);
|
||||
o.c = unpack_color(e.x);
|
||||
o.h = e.y;
|
||||
return o;
|
||||
}
|
||||
|
||||
@@ -141,6 +162,7 @@ fn flat_fragment(i: FOut, front: bool) -> TOut {
|
||||
o.pos = i.pos;
|
||||
o.n = n;
|
||||
o.c = i.c;
|
||||
o.h = i.h;
|
||||
o.wp = i.rp + u.eye.xyz;
|
||||
return o;
|
||||
}
|
||||
@@ -209,6 +231,12 @@ fn fs_ao_depth_flat(i: FOut, @builtin(front_facing) front: bool) -> AoDepth {
|
||||
return opaque_depth(t, front);
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_ao_term_flat(i: FOut, @builtin(front_facing) front: bool) -> @location(0) vec4<f32> {
|
||||
let t = flat_fragment(i, front);
|
||||
return ao_term(t, front);
|
||||
}
|
||||
|
||||
/// Opaque model surfaces: cut by the section plane/// Opaque model surfaces: cut by the section plane, the inside shows as a flat cap colour.
|
||||
@fragment
|
||||
fn fs_solid(i: TOut, @builtin(front_facing) front: bool) -> @location(0) vec4<f32> {
|
||||
@@ -216,13 +244,43 @@ fn fs_solid(i: TOut, @builtin(front_facing) front: bool) -> @location(0) vec4<f3
|
||||
return c;
|
||||
}
|
||||
|
||||
/// A section's crossing count (`Cut::count` in `view.rs`): nothing but the cut itself, so the
|
||||
/// stencil counts only the faces behind the section plane. The colour is not written.
|
||||
@fragment
|
||||
fn fs_clip(i: TOut) -> @location(0) vec4<f32> {
|
||||
if (clipped(i.wp)) { discard; }
|
||||
return vec4<f32>(0.0);
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_clip_flat(i: FOut) -> @location(0) vec4<f32> {
|
||||
if (clipped(i.rp + u.eye.xyz)) { discard; }
|
||||
return vec4<f32>(0.0);
|
||||
}
|
||||
|
||||
/// Faces where the section plane is outside every closed body (`Cut::rest`): a back face seen
|
||||
/// there is the inside of an open body (a surface body, a solid with faces left out), lit as it
|
||||
/// is without a section rather than hatched, since there is no cut face to show.
|
||||
@fragment
|
||||
fn fs_solid_open(i: TOut, @builtin(front_facing) front: bool) -> @location(0) vec4<f32> {
|
||||
let c = solid(i, true);
|
||||
return c;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_solid_open_flat(i: FOut, @builtin(front_facing) front: bool) -> @location(0) vec4<f32> {
|
||||
let t = flat_fragment(i, front);
|
||||
let c = solid(t, true);
|
||||
return c;
|
||||
}
|
||||
|
||||
struct CutOut {
|
||||
@location(0) c: vec4<f32>,
|
||||
@builtin(frag_depth) depth: f32,
|
||||
};
|
||||
|
||||
/// The section cap (the inside seen through the cut, drawn on the far walls' back faces where
|
||||
/// they are the visible surface) again, at the depth of the section plane rather than of the
|
||||
/// The section cap (where the section plane lies inside a body, drawn on the far walls' back
|
||||
/// faces) again, at the depth of the section plane rather than of the
|
||||
/// far wall, so the cut face hides what lies inside the body instead of showing it like a
|
||||
/// pane of glass.
|
||||
@fragment
|
||||
@@ -244,44 +302,6 @@ fn pushed(z: f32) -> f32 {
|
||||
return clamp(z - select(-push, push, reversed()), 0.0, 1.0);
|
||||
}
|
||||
|
||||
/// A far wall seen through the cut where another body's face lying on it was drawn in front of
|
||||
/// it (a shaft in its bore, #9407): its cap colour again, at its depth pulled toward the eye by
|
||||
/// `touch_depth`, so it wins over that face, which is faceted another way.
|
||||
@fragment
|
||||
fn fs_solid_touch(i: TOut, @builtin(front_facing) front: bool) -> CutOut {
|
||||
var o: CutOut;
|
||||
// Derivatives (in `touch_depth`) first, before `solid` may discard.
|
||||
o.depth = touch_depth(i.wp, i.pos.z);
|
||||
o.c = solid(i, front);
|
||||
return o;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_solid_touch_flat(i: FOut, @builtin(front_facing) front: bool) -> CutOut {
|
||||
let t = flat_fragment(i, front);
|
||||
var o: CutOut;
|
||||
o.depth = touch_depth(t.wp, t.pos.z);
|
||||
o.c = solid(t, front);
|
||||
return o;
|
||||
}
|
||||
|
||||
/// The depth of `wp` (depth `z`) pulled toward the eye by what may part two faceted copies of
|
||||
/// one surface: 3 pixels' width along the view ray (display refinement keeps a facet's sag about
|
||||
/// a pixel) and 2 pixels of the face's own depth slope (more where it turns from the eye).
|
||||
fn touch_depth(wp: vec3<f32>, z: f32) -> f32 {
|
||||
let px = min(length(dpdx(wp)), length(dpdy(wp)));
|
||||
let slope = max(abs(dpdx(z)), abs(dpdy(z)));
|
||||
var dir = -u.back.xyz;
|
||||
if (u.eye.w > 0.5) { dir = wp - u.eye.xyz; }
|
||||
let len = length(dir);
|
||||
if (len < 1e-12) { return z; }
|
||||
let c = u.vp * vec4<f32>(wp - dir * (3.0 * px / len), 1.0);
|
||||
if (c.w <= 0.0) { return z; }
|
||||
let pulled = c.z / c.w;
|
||||
if (reversed()) { return clamp(max(pulled, z) + 2.0 * slope, 0.0, 1.0); }
|
||||
return clamp(min(pulled, z) - 2.0 * slope, 0.0, 1.0);
|
||||
}
|
||||
|
||||
/// Depth of the section plane on the view ray through `wp` (a far wall seen through the cut),
|
||||
/// never behind the wall itself (`z`).
|
||||
fn cap_depth(wp: vec3<f32>, z: f32) -> f32 {
|
||||
@@ -305,11 +325,19 @@ fn solid(i: TOut, front: bool) -> vec4<f32> {
|
||||
if (clipped(i.wp)) { discard; }
|
||||
if (!front && length(u.clip.xyz) > 0.5) {
|
||||
// The cut face in its body's colour ("from component" colouring), tinted toward the
|
||||
// cap colour so it reads as a cut, hatched at 45 degrees on screen; with the hatch off
|
||||
// cap colour so it reads as a cut, hatched at 45 degrees on screen; by part, toward the
|
||||
// body's own hatch colour and at its angle (45 or 135 degrees). With the hatch off
|
||||
// (`u.cap.a` 0), plain in the body's own colour.
|
||||
if (u.cap.a < 0.5) { return out_color(vec4<f32>(i.c.rgb, 1.0)); }
|
||||
let base = mix(i.c.rgb, u.cap.rgb, 0.35);
|
||||
let h = fract((i.pos.x + i.pos.y) / 9.0);
|
||||
var tint = u.cap.rgb;
|
||||
var turn = 1.0;
|
||||
if (i.h > 0u) {
|
||||
let k = i.h - 1u;
|
||||
tint = u.hatch[min(k >> 1u, 2u)].rgb;
|
||||
turn = select(1.0, -1.0, (k & 1u) == 1u);
|
||||
}
|
||||
let base = mix(i.c.rgb, tint, 0.35);
|
||||
let h = fract((i.pos.x + turn * i.pos.y) / 9.0);
|
||||
return out_color(vec4<f32>(select(base, base * 0.6, h < 0.22), 1.0));
|
||||
}
|
||||
if (u.ana.x > 0.5 && length(i.n) > 0.5) {
|
||||
@@ -346,11 +374,89 @@ fn opaque_depth(i: TOut, front: bool) -> AoDepth {
|
||||
return AoDepth(depth, bitcast<u32>(depth));
|
||||
}
|
||||
|
||||
/// The term for an opaque surface's fragment, on the prepass's depth. A section's cut face has none.
|
||||
fn ao_term(i: TOut, front: bool) -> vec4<f32> {
|
||||
if (clipped(i.wp) || length(i.n) < 0.5) { discard; }
|
||||
if (!front && length(u.clip.xyz) > 0.5) { return vec4<f32>(1.0); }
|
||||
return vec4<f32>(occlusion_term(i));
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_ao_term(i: TOut, @builtin(front_facing) front: bool) -> @location(0) vec4<f32> {
|
||||
return ao_term(i, front);
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vs_ao_blur(@builtin(vertex_index) v: u32) -> @builtin(position) vec4<f32> {
|
||||
let p = vec2<f32>(f32((v << 1u) & 2u), f32(v & 2u));
|
||||
return vec4<f32>(p * 2.0 - 1.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
/// The depth map at `p`, reading 0 near and 1 far whichever way the buffer runs.
|
||||
fn map_depth(p: vec2<i32>) -> f32 {
|
||||
let at = clamp(p, vec2<i32>(0), vec2<i32>(textureDimensions(ao_depth)) - vec2<i32>(1));
|
||||
let d = bitcast<f32>(textureLoad(ao_depth, at, 0).x);
|
||||
return select(d, 1.0 - d, reversed());
|
||||
}
|
||||
|
||||
/// The raw term (`ao_map` in this pass) averaged over a 4 x 4 window, which holds every turn of
|
||||
/// [`ao_turn`]'s pattern once. A neighbour counts when it lies on this pixel's plane, the slope
|
||||
/// taken from the nearer side across each axis, so no surface's shade crosses an edge.
|
||||
@fragment
|
||||
fn fs_ao_blur(@builtin(position) pos: vec4<f32>) -> @location(0) vec4<f32> {
|
||||
let c = vec2<i32>(pos.xy);
|
||||
let d = map_depth(c);
|
||||
if (d >= 1.0) { return vec4<f32>(1.0); }
|
||||
let left = d - map_depth(c + vec2<i32>(-1, 0));
|
||||
let right = map_depth(c + vec2<i32>(1, 0)) - d;
|
||||
let above = d - map_depth(c + vec2<i32>(0, -1));
|
||||
let below = map_depth(c + vec2<i32>(0, 1)) - d;
|
||||
let dx = select(right, left, abs(left) < abs(right));
|
||||
let dy = select(below, above, abs(above) < abs(below));
|
||||
let tolerance = 1.5 * (abs(dx) + abs(dy)) + 1e-6;
|
||||
let last = vec2<i32>(textureDimensions(ao_map)) - vec2<i32>(1);
|
||||
var sum = 0.0;
|
||||
var weight = 0.0;
|
||||
for (var y = -2; y < 2; y = y + 1) {
|
||||
for (var x = -2; x < 2; x = x + 1) {
|
||||
let p = c + vec2<i32>(x, y);
|
||||
let s = map_depth(p);
|
||||
if (s < 1.0 && abs(s - (d + dx * f32(x) + dy * f32(y))) <= tolerance) {
|
||||
sum = sum + textureLoad(ao_map, clamp(p, vec2<i32>(0), last), 0).x;
|
||||
weight = weight + 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return vec4<f32>(sum / max(weight, 1.0));
|
||||
}
|
||||
|
||||
/// A pixel's turn of the sample pattern: every 4 x 4 window holds each of 16 turns once.
|
||||
fn ao_turn(pos: vec2<f32>) -> f32 {
|
||||
let x = u32(pos.x) & 3u;
|
||||
let y = u32(pos.y) & 3u;
|
||||
// WGSL won't mix shifts and `|` without parentheses; naga takes it, browsers refuse it (#11080).
|
||||
let v = (((x ^ y) & 1u) << 3u) | ((y & 1u) << 2u) | (((x ^ y) >> 1u) << 1u) | (y >> 1u);
|
||||
return (f32(v) + 0.5) / 16.0;
|
||||
}
|
||||
|
||||
/// The lit pass's occlusion: the blurred term where this point falls in the map, whatever the
|
||||
/// window's viewport offset and supersampling.
|
||||
fn occlusion(i: TOut) -> f32 {
|
||||
let size = vec2<i32>(textureDimensions(ao_map));
|
||||
// The maps are 1 × 1 stand-ins until a frame draws into them (the product preview draws no
|
||||
// prepass, and AO off releases them): they tell nothing, so leave the surface unoccluded.
|
||||
if (u.ao.x <= 0.0 || length(i.n) < 0.5 || size.x < 2 || size.y < 2) { return 1.0; }
|
||||
let q = u.vp * vec4<f32>(i.wp, 1.0);
|
||||
if (q.w <= 0.0) { return 1.0; }
|
||||
let uv = vec2<f32>(q.x / q.w * 0.5 + 0.5, 0.5 - q.y / q.w * 0.5);
|
||||
return textureLoad(ao_map, clamp(vec2<i32>(uv * vec2<f32>(size)), vec2<i32>(0), size - vec2<i32>(1)), 0).x;
|
||||
}
|
||||
|
||||
// Cosine-weighted hemisphere samples, the pattern turned by a different angle at each pixel.
|
||||
// Projecting the sample and a point one radius toward the eye supplies a depth range in both
|
||||
// orthographic and perspective views; distant foreground geometry cannot cast a dark halo onto
|
||||
// a background surface.
|
||||
fn occlusion(i: TOut) -> f32 {
|
||||
fn occlusion_term(i: TOut) -> f32 {
|
||||
let radius = u.ao.x;
|
||||
if (radius <= 0.0 || length(i.n) < 0.5) { return 1.0; }
|
||||
var n = normalize(i.n);
|
||||
@@ -364,10 +470,9 @@ fn occlusion(i: TOut) -> f32 {
|
||||
if (size.x < 2 || size.y < 2) { return 1.0; }
|
||||
// The same offsets at every pixel made each sample darken its own shifted copy of a nearby
|
||||
// part's outline: faint ghosts of the part where the radius is large on screen (#4167),
|
||||
// stepped bands along contacts where it is small (#3407). Turned by interleaved gradient
|
||||
// noise (Jimenez 2014), neighbouring pixels sample different points and the copies break up
|
||||
// into an even grain.
|
||||
let turn = fract(52.9829189 * fract(dot(i.pos.xy, vec2<f32>(0.06711056, 0.00583715))));
|
||||
// stepped bands along contacts where it is small (#3407). Turned, neighbouring pixels sample
|
||||
// different points, the copies break up into grain, and the blur takes the grain out (#9327).
|
||||
let turn = ao_turn(i.pos.xy);
|
||||
let t = tangent * cos(turn * 6.28318531) + bitangent * sin(turn * 6.28318531);
|
||||
let b = cross(n, t);
|
||||
// One comparison for both depth directions: depths read as 0 near / 1 far (mirrored when
|
||||
|
||||
@@ -75,6 +75,7 @@ impl Tokens {
|
||||
font_compass: 13.5,
|
||||
font_subtitle: 15.0,
|
||||
font_intro: 16.0,
|
||||
font_page_heading: 20.0,
|
||||
font_article_heading: 17.0,
|
||||
font_title: 22.0,
|
||||
font_display: 26.0,
|
||||
@@ -203,6 +204,11 @@ impl Tokens {
|
||||
dimension_driven: Color32::from_rgb(160, 90, 120),
|
||||
sketch_point_free: Color32::from_rgb(30, 90, 200),
|
||||
section_cap: Color32::from_rgb(214, 128, 72),
|
||||
// A steel and a sage that tint a body about as strongly as the cap does and to within
|
||||
// a few points of its lightness, so the cut stays calm, while staying apart from the
|
||||
// cap, each other and the body in every colour vision mode (dark keeps them, as the
|
||||
// cap).
|
||||
section_hatch: [Color32::from_rgb(84, 138, 168), Color32::from_rgb(108, 180, 126)],
|
||||
dialog_bg: Color32::from_rgb(250, 251, 252),
|
||||
dialog_border: Color32::from_rgb(213, 219, 227),
|
||||
chip: Color32::from_rgb(38, 120, 218),
|
||||
@@ -228,6 +234,8 @@ impl Tokens {
|
||||
selected_bg: Color32::from_rgb(232, 241, 250),
|
||||
figure_ground: Color32::from_rgb(247, 248, 250),
|
||||
figure_line: Color32::from_rgb(111, 124, 143),
|
||||
figure_highlight: Color32::WHITE,
|
||||
figure_shadow: Color32::BLACK,
|
||||
figure_face_top: Color32::from_rgb(255, 255, 255),
|
||||
figure_face_left: Color32::from_rgb(228, 233, 240),
|
||||
figure_face_right: Color32::from_rgb(201, 210, 222),
|
||||
@@ -241,8 +249,9 @@ impl Tokens {
|
||||
figure_axis_z: Color32::from_rgb(47, 111, 208),
|
||||
figure_ok: Color32::from_rgb(47, 125, 91),
|
||||
figure_warn: Color32::from_rgb(201, 138, 0),
|
||||
figure_conflict: Color32::from_rgb(184, 74, 0),
|
||||
pref_well: Color32::from_rgb(238, 241, 245),
|
||||
pref_note: Color32::from_rgb(184, 74, 0),
|
||||
pref_note: Color32::from_rgb(183, 74, 0),
|
||||
pref_note_fill: Color32::from_rgb(251, 235, 221),
|
||||
pref_on_accent: Color32::from_rgb(255, 255, 255),
|
||||
pref_ok: Color32::from_rgb(39, 107, 44),
|
||||
@@ -263,6 +272,8 @@ impl Tokens {
|
||||
bend_up: Color32::from_rgb(30, 140, 50),
|
||||
bend_down: Color32::from_rgb(200, 90, 20),
|
||||
interference: Color32::from_rgb(235, 45, 40),
|
||||
comb_envelope: Color32::from_rgb(130, 40, 175),
|
||||
comb_teeth: Color32::from_rgb(215, 105, 0),
|
||||
continuity: [
|
||||
Color32::from_rgb(0, 158, 115),
|
||||
Color32::from_rgb(0, 114, 178),
|
||||
@@ -380,6 +391,7 @@ impl Tokens {
|
||||
t.figure_axis_z = Color32::from_rgb(108, 168, 247);
|
||||
t.figure_ok = Color32::from_rgb(127, 209, 174);
|
||||
t.figure_warn = Color32::from_rgb(232, 194, 74);
|
||||
t.figure_conflict = Color32::from_rgb(240, 162, 122);
|
||||
t.pref_well = Color32::from_rgb(47, 52, 62);
|
||||
t.pref_note = Color32::from_rgb(240, 162, 122);
|
||||
t.pref_note_fill = Color32::from_rgb(74, 51, 38);
|
||||
@@ -429,6 +441,8 @@ impl Tokens {
|
||||
t.box_crossing = Color32::from_rgb(80, 200, 120);
|
||||
t.bend_up = Color32::from_rgb(110, 200, 120);
|
||||
t.bend_down = Color32::from_rgb(240, 150, 80);
|
||||
t.comb_envelope = Color32::from_rgb(195, 130, 255);
|
||||
t.comb_teeth = Color32::from_rgb(255, 160, 60);
|
||||
t.form_ink = t.text;
|
||||
t.form_dim = Color32::from_rgb(180, 187, 200);
|
||||
t.form_primary = t.accent;
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
"chip": "#ffffff",
|
||||
"construction_geom": "#ffffff",
|
||||
"construction_plane": "#000000",
|
||||
"comb_envelope": "#ffffff",
|
||||
"comb_teeth": "#ffffff",
|
||||
"continuity": [
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
@@ -132,6 +134,10 @@
|
||||
"render_soft": "#000000",
|
||||
"rubber_band": "#ffffff",
|
||||
"section_cap": "#ffffff",
|
||||
"section_hatch": [
|
||||
"#ffffff",
|
||||
"#ffffff"
|
||||
],
|
||||
"sel_edge": "#000000",
|
||||
"sel_edge_rim": "#000000",
|
||||
"sel_face": "#ffffff",
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
"chip": "#000000",
|
||||
"construction_geom": "#000000",
|
||||
"construction_plane": "#ffffff",
|
||||
"comb_envelope": "#000000",
|
||||
"comb_teeth": "#000000",
|
||||
"continuity": [
|
||||
"#000000",
|
||||
"#000000",
|
||||
@@ -132,6 +134,10 @@
|
||||
"render_soft": "#ffffff",
|
||||
"rubber_band": "#000000",
|
||||
"section_cap": "#000000",
|
||||
"section_hatch": [
|
||||
"#000000",
|
||||
"#000000"
|
||||
],
|
||||
"sel_edge": "#ffffff",
|
||||
"sel_edge_rim": "#ffffff",
|
||||
"sel_face": "#000000",
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
"chip": "#aa00aa",
|
||||
"construction_geom": "#aaaaaa",
|
||||
"construction_plane": "#ffff5522",
|
||||
"comb_envelope": "#ff55ff",
|
||||
"comb_teeth": "#55ffff",
|
||||
"continuity": [
|
||||
"#55ff55",
|
||||
"#5555ff",
|
||||
@@ -132,6 +134,10 @@
|
||||
"render_soft": "#000000",
|
||||
"rubber_band": "#55ffff",
|
||||
"section_cap": "#ffff55",
|
||||
"section_hatch": [
|
||||
"#555555",
|
||||
"#aaaaaa"
|
||||
],
|
||||
"sel_edge": "#ffff55",
|
||||
"sel_edge_rim": "#000000",
|
||||
"sel_face": "#ffff55",
|
||||
|
||||
@@ -12,6 +12,7 @@ workspace = true
|
||||
solidcraft-geom = { workspace = true }
|
||||
solidcraft-sketch = { workspace = true }
|
||||
solidcraft-kernel = { workspace = true }
|
||||
solidcraft-profile = { workspace = true }
|
||||
solidcraft-render = { workspace = true }
|
||||
solidcraft-derive = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
|
||||
@@ -6,7 +6,7 @@ pub fn state_in_face_anchor_frame<'a>(doc: &Document, state: &'a ModelState, anc
|
||||
let frame = anchor.author_frame(doc, author)?;
|
||||
let raw = state.body(&anchor.target.body).ok_or_else(|| DocError::Unknown(format!("anchored body `{}`", anchor.target.body)))?;
|
||||
let relative = anchor.source_to_author(doc, author, raw)?;
|
||||
let mut output = crate::state_in_frame_at(doc, state, author, frame).into_owned();
|
||||
let mut output = crate::state_in_frame_at(doc, state, author, frame)?.into_owned();
|
||||
// In place, the body is the model's own, display mesh included: a sketch projecting the
|
||||
// face's edges meshes it once for the features after it too, rather than a copy.
|
||||
let body = state.body_in_frame(raw, &relative)?;
|
||||
|
||||
@@ -84,6 +84,23 @@ pub fn mat_inverse(m: &Mat) -> Option<Mat> {
|
||||
Some(out)
|
||||
}
|
||||
|
||||
/// Whether `m` is a placement an occurrence may have: finite, a rotation (orthonormal axes, not
|
||||
/// mirrored) and a translation within 1e9 mm, its last column `0, 0, 0, 1`. Every placement is
|
||||
/// checked against this where it enters the document (a design file, a command, an import).
|
||||
pub fn is_placement(m: &Mat) -> bool {
|
||||
let axis = |i: usize| Vec3::new(m[i][0], m[i][1], m[i][2]);
|
||||
let (x, y, z) = (axis(0), axis(1), axis(2));
|
||||
m.iter().flatten().all(|v| v.is_finite())
|
||||
&& m[3][..3].iter().all(|v| v.abs() <= 1e9)
|
||||
&& [x, y, z].iter().all(|v| (v.dot(*v) - 1.0).abs() < 1e-6)
|
||||
&& x.dot(y).abs() < 1e-6
|
||||
&& x.dot(z).abs() < 1e-6
|
||||
&& y.dot(z).abs() < 1e-6
|
||||
&& x.cross(y).dot(z) > 0.0
|
||||
&& [m[0][3], m[1][3], m[2][3]].iter().all(|v| v.abs() < 1e-9)
|
||||
&& (m[3][3] - 1.0).abs() < 1e-9
|
||||
}
|
||||
|
||||
/// A rigid transform: rotation by `angle` about `axis` through `origin`, then a translation.
|
||||
pub fn rigid(translate: Vec3, origin: Vec3, axis: Vec3, angle: f64) -> Mat {
|
||||
let k = axis.normalized().unwrap_or(Vec3::Z);
|
||||
@@ -202,9 +219,12 @@ fn map_plane_ref(m: &Mat, p: &mut PlaneRef) {
|
||||
}
|
||||
// Named origin planes and construction planes are the component's own.
|
||||
PlaneRef::Origin { .. } | PlaneRef::Construction { .. } => {}
|
||||
PlaneRef::Tangent { face, at } => {
|
||||
PlaneRef::Tangent { face, at, reference, .. } => {
|
||||
*at = apply_point(m, *at);
|
||||
map_geo_ref(m, face);
|
||||
if let Some(r) = reference {
|
||||
map_geo_ref(m, r);
|
||||
}
|
||||
}
|
||||
PlaneRef::Midplane { .. }
|
||||
| PlaneRef::TwoEdges { .. }
|
||||
@@ -456,7 +476,8 @@ impl Document {
|
||||
false
|
||||
}
|
||||
|
||||
/// Add an occurrence of an existing component into `parent`.
|
||||
/// Add an occurrence of an existing component into `parent`, placed by `transform` (which
|
||||
/// must pass [`is_placement`]).
|
||||
pub fn add_occurrence(&mut self, component: u64, parent: u64, transform: Mat) -> Result<u64> {
|
||||
if component == 0 || !self.components.iter().any(|c| c.id == component) {
|
||||
return Err(DocError::Unknown(format!("component {component}")));
|
||||
@@ -467,6 +488,9 @@ impl Document {
|
||||
if self.occurrences.len() >= 100_000 {
|
||||
return Err(DocError::Invalid("too many occurrences".into()));
|
||||
}
|
||||
if !is_placement(&transform) {
|
||||
return Err(DocError::Invalid("an occurrence's placement must be a rigid motion (no scaling or mirroring) within 1e9 mm".into()));
|
||||
}
|
||||
let id = self.next_id.max(1);
|
||||
self.next_id = id.checked_add(1).ok_or_else(|| DocError::Invalid("too many items in the design".into()))?;
|
||||
let cname = self.components.iter().find(|c| c.id == component).map(|c| c.name.clone()).unwrap_or_default();
|
||||
|
||||
@@ -1,20 +1,9 @@
|
||||
//! Recorded ownership changes are evaluated at their timeline position.
|
||||
use crate::{DocError, Document, Mat, ModelBody, ModelState, Result};
|
||||
use solidcraft_geom::Vec3;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
pub fn valid_transfer_matrix(m: &Mat) -> bool {
|
||||
let axis = |i: usize| Vec3::new(m[i][0], m[i][1], m[i][2]);
|
||||
let (x, y, z) = (axis(0), axis(1), axis(2));
|
||||
m.iter().flatten().all(|v| v.is_finite())
|
||||
&& m[3][..3].iter().all(|v| v.abs() <= 1e9)
|
||||
&& [x, y, z].iter().all(|v| (v.dot(*v) - 1.0).abs() < 1e-6)
|
||||
&& x.dot(y).abs() < 1e-6
|
||||
&& x.dot(z).abs() < 1e-6
|
||||
&& y.dot(z).abs() < 1e-6
|
||||
&& x.cross(y).dot(z) > 0.0
|
||||
&& [m[0][3], m[1][3], m[2][3]].iter().all(|v| v.abs() < 1e-9)
|
||||
&& (m[3][3] - 1.0).abs() < 1e-9
|
||||
crate::is_placement(m)
|
||||
}
|
||||
|
||||
/// Validate the stored boundary before allocation, loading, or evaluation.
|
||||
|
||||
@@ -207,6 +207,7 @@ impl crate::Feature {
|
||||
K::Draft { neutral, .. } => checked_plane(neutral, 0)?,
|
||||
K::ReplaceFace { target, .. } => checked_plane(target, 0)?,
|
||||
K::SurfaceExtend { up_to: Some(crate::ExtendTo::Plane(plane)), .. } => checked_plane(plane, 0)?,
|
||||
K::Thread { up_to: Some(crate::ThreadLimit::Plane(plane)), .. } => checked_plane(plane, 0)?,
|
||||
K::ConstructionAxis { def } => {
|
||||
for r in def.refs() {
|
||||
checked_geo(r, 0)?;
|
||||
@@ -276,6 +277,7 @@ impl crate::Feature {
|
||||
K::Draft { neutral, .. } => remap_plane(neutral, ids, bodies, 0)?,
|
||||
K::ReplaceFace { target, .. } => remap_plane(target, ids, bodies, 0)?,
|
||||
K::SurfaceExtend { up_to: Some(crate::ExtendTo::Plane(plane)), .. } => remap_plane(plane, ids, bodies, 0)?,
|
||||
K::Thread { up_to: Some(crate::ThreadLimit::Plane(plane)), .. } => remap_plane(plane, ids, bodies, 0)?,
|
||||
K::ConstructionAxis { def } => {
|
||||
for r in def.refs_mut() {
|
||||
remap_geo(r, ids, bodies, 0)?;
|
||||
@@ -333,7 +335,12 @@ fn checked_plane(p: &PlaneRef, depth: usize) -> crate::Result<()> {
|
||||
checked_geo(b, depth + 1)?;
|
||||
checked_geo(c, depth + 1)?;
|
||||
}
|
||||
PlaneRef::Tangent { face, .. } => checked_geo(face, depth + 1)?,
|
||||
PlaneRef::Tangent { face, reference, .. } => {
|
||||
checked_geo(face, depth + 1)?;
|
||||
if let Some(r) = reference {
|
||||
checked_geo(r, depth + 1)?;
|
||||
}
|
||||
}
|
||||
PlaneRef::AlongPath { path, .. } => checked_geo(path, depth + 1)?,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -842,8 +842,13 @@ impl Document {
|
||||
};
|
||||
let mut indices: Vec<usize> = names.faces.iter().enumerate().filter(|(_, face)| piece_of(face)).map(|(index, _)| index).collect();
|
||||
if indices.is_empty() {
|
||||
indices =
|
||||
names.info.iter().enumerate().filter(|(_, info)| info.aliases.iter().any(|alias| alias == name)).map(|(index, _)| index).collect();
|
||||
indices = names
|
||||
.info
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, info)| info.synonyms.iter().chain(&info.aliases).any(|other| other == name))
|
||||
.map(|(index, _)| index)
|
||||
.collect();
|
||||
}
|
||||
if indices.is_empty() {
|
||||
return Err(DocError::Unknown(format!("face `{name}` on `{}` no longer exists", body.name)));
|
||||
|
||||
@@ -221,7 +221,7 @@ fn regularized_shell_keeps_legacy_and_explicit_single_face_projections_closed()
|
||||
let mut sketch = solidcraft_sketch::Sketch::new();
|
||||
let link = sketch.add_link(solidcraft_sketch::LinkKind::Project, source, &legacy.geom).unwrap();
|
||||
sketch.links[0].face_name = Some(crate::naming::face_names(body)[index].clone());
|
||||
let warnings = crate::project::refresh_links(&doc, &values, &state, &plane, &mut sketch);
|
||||
let warnings = crate::project::refresh_links(&doc, &values, &state, &plane, &mut sketch).unwrap();
|
||||
assert!(warnings.is_empty(), "{warnings:?}");
|
||||
assert!(!sketch.links[0].lost);
|
||||
assert!(!sketch.update_link(&link, &explicit.geom).unwrap());
|
||||
@@ -230,8 +230,8 @@ fn regularized_shell_keeps_legacy_and_explicit_single_face_projections_closed()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saved_former_strip_projection_reports_lost_without_expanding_healthy_material() {
|
||||
let (mut doc, _, shell) = shell_document();
|
||||
fn saved_former_strip_projection_asks_for_its_face_again_without_expanding_healthy_material() {
|
||||
let (mut doc, _, _) = shell_document();
|
||||
let mut model = Model::new();
|
||||
model.evaluate(&doc);
|
||||
let state = model.state();
|
||||
@@ -240,27 +240,10 @@ fn saved_former_strip_projection_reports_lost_without_expanding_healthy_material
|
||||
let plane = body.body.face_plane(index).unwrap();
|
||||
let stem = crate::naming::face_names(body)[index].clone();
|
||||
assert_eq!(crate::naming::strip_piece(&stem), stem);
|
||||
// Reconstruct the original raw shell before certificate stamping/regularization,
|
||||
// and run the unchanged historical naming/capture path on its actual strip.
|
||||
let before = model.state_before(shell);
|
||||
// Legacy Shell did not propagate construction certificates. Omit transient
|
||||
// metadata here; operand paint also disables new wall-run coalescence so
|
||||
// this control retains the actual historical raw pieces.
|
||||
let raw_source = solidcraft_kernel::box_solid(Vec3::ZERO, Vec3::new(20., 12., 10.)).unwrap().with_paint(Some(solidcraft_kernel::Paint {
|
||||
opacity: 1.,
|
||||
faces: vec![solidcraft_kernel::FacePaint { face: 0, color: [1., 0., 0.], opacity: 1. }],
|
||||
}));
|
||||
let raw = solidcraft_kernel::shell(&raw_source, &[Vec3::new(5., 6., 10.)], 2.).unwrap();
|
||||
let mut old_body = ModelBody::new("Case".into(), raw, shell);
|
||||
old_body.names = crate::naming::NameCell::new(Some(std::sync::Arc::new(crate::naming::NamingOrigin {
|
||||
feature: doc.feature(shell).unwrap().clone(),
|
||||
before,
|
||||
depth: 1,
|
||||
mats: vec![],
|
||||
sheets: vec![],
|
||||
})));
|
||||
let captured = crate::naming::face_name_at_body(&old_body, Vec3::new(20., 6., 1.)).unwrap();
|
||||
assert_eq!(crate::naming::parent_piece(&captured), Some(stem.as_str()), "actual strip capture records a piece name");
|
||||
// The builds before format 14 split this wall into strips along the opening and saved a
|
||||
// projection of one of them by its number. The kernel's shell leaves the wall one face now,
|
||||
// so the strip exists only as that saved name and geometry.
|
||||
assert_eq!(crate::naming::face_name_at_body(body, Vec3::new(20., 6., 1.)).as_deref(), Some(stem.as_str()), "the shelled wall is one face");
|
||||
// What the builds before format 14 saved for that piece: its number by centroid order.
|
||||
let former = format!("{stem}#1");
|
||||
let mut sketch = solidcraft_sketch::Sketch::new();
|
||||
@@ -274,10 +257,9 @@ fn saved_former_strip_projection_reports_lost_without_expanding_healthy_material
|
||||
)
|
||||
.unwrap();
|
||||
sketch.links[0].face_name = Some(former.clone());
|
||||
let original = sketch.clone();
|
||||
let sk = doc
|
||||
.add_feature(
|
||||
FeatureKind::Sketch { plane: PlaneRef::Face { plane, at: Vec3::new(20., 6., 1.), name: Some(former), anchor: None }, sketch },
|
||||
FeatureKind::Sketch { plane: PlaneRef::Face { plane, at: Vec3::new(20., 6., 1.), name: Some(former.clone()), anchor: None }, sketch },
|
||||
Some("Saved strip sketch"),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -305,28 +287,19 @@ fn saved_former_strip_projection_reports_lost_without_expanding_healthy_material
|
||||
let mut cold = Model::new();
|
||||
cold.evaluate(&loaded);
|
||||
for current in [&model, &cold] {
|
||||
let warning = current.results.iter().find(|r| r.id == sk).unwrap().warning.as_deref().unwrap();
|
||||
assert!(warning.contains("no longer exists; last geometry retained"), "{warning}");
|
||||
let state = current.state();
|
||||
let solved = state.sketch(sk).unwrap();
|
||||
assert!(solved.sketch.links[0].lost);
|
||||
assert_eq!(solved.sketch.curves, original.curves);
|
||||
assert_eq!(solved.sketch.points, original.points);
|
||||
let material = state.bodies.iter().find(|body| body.feature == boss).unwrap();
|
||||
assert!(
|
||||
(solidcraft_kernel::measure(&material.body).unwrap().volume - 24.).abs() < 1e-8,
|
||||
"old24mm3 profile must not silently become120mm3 wall profile"
|
||||
);
|
||||
let bounds = material.mesh().bounds();
|
||||
assert!((bounds.min.x - width).abs() < 1e-8 && (bounds.max.x - width - 1.).abs() < 1e-8);
|
||||
assert!((bounds.max.y - 12.).abs() < 1e-8 && (bounds.max.z - 2.).abs() < 1e-8);
|
||||
// The name was saved before format 14 (marked): it finds no face, so the sketch on it
|
||||
// fails, asking for its face again, and so does the material made from it.
|
||||
let result = current.results.iter().find(|r| r.id == sk).unwrap();
|
||||
let reference = format!("{}{former}", crate::naming::LEGACY_MARK);
|
||||
assert_eq!(result.error.as_deref().and_then(crate::reselect_parts), Some(("Saved strip sketch", reference.as_str())), "{result:?}");
|
||||
assert!(current.results.iter().find(|r| r.id == boss).unwrap().error.is_some());
|
||||
assert!(current.state().bodies.iter().all(|body| body.feature != boss), "old 24 mm3 profile must not silently become the 120 mm3 wall");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saved_curved_strip_projection_is_lost_after_exact_wall_coalescence() {
|
||||
use solidcraft_kernel::{FacePaint, Paint};
|
||||
fn saved_curved_strip_projection_asks_for_its_face_again_after_exact_wall_coalescence() {
|
||||
use solidcraft_sketch::{LinkKind, LinkSource, Sketch};
|
||||
let mut doc = Document::new("saved curved strip");
|
||||
doc.set_param("width", "80", Some("mm"), None).unwrap();
|
||||
@@ -344,90 +317,43 @@ fn saved_curved_strip_projection_is_lost_after_exact_wall_coalescence() {
|
||||
Some("Corners"),
|
||||
)
|
||||
.unwrap();
|
||||
let shell = doc
|
||||
.add_feature(
|
||||
FeatureKind::Shell {
|
||||
faces: vec![crate::FacePick(Vec3::new(0., 0., 30.))],
|
||||
thickness: "wall".into(),
|
||||
body: Some("Case".into()),
|
||||
direction: "inside".into(),
|
||||
tangent_chain: false,
|
||||
},
|
||||
Some("Shell"),
|
||||
)
|
||||
.unwrap();
|
||||
doc.add_feature(
|
||||
FeatureKind::Shell {
|
||||
faces: vec![crate::FacePick(Vec3::new(0., 0., 30.))],
|
||||
thickness: "wall".into(),
|
||||
body: Some("Case".into()),
|
||||
direction: "inside".into(),
|
||||
tangent_chain: false,
|
||||
},
|
||||
Some("Shell"),
|
||||
)
|
||||
.unwrap();
|
||||
let mut model = Model::new();
|
||||
model.evaluate(&doc);
|
||||
assert!(model.results.iter().all(|r| r.error.is_none()), "{:?}", model.results);
|
||||
let before = model.state_before(shell);
|
||||
// Per-face paint disables the construction optimization: this control keeps
|
||||
// the real pre-repair slab faces without forging a name or source witness.
|
||||
let old_source = before
|
||||
.body("Case")
|
||||
.unwrap()
|
||||
.body
|
||||
.clone()
|
||||
.with_paint(Some(Paint { opacity: 1., faces: vec![FacePaint { face: 0, color: [1., 0., 0.], opacity: 1. }] }));
|
||||
let cavity_box = solidcraft_kernel::box_solid(Vec3::new(-38., -28., 2.), Vec3::new(38., 28., 35.)).unwrap();
|
||||
let cavity = solidcraft_kernel::fillet(
|
||||
&cavity_box,
|
||||
&[Vec3::new(38., 28., 15.), Vec3::new(-38., 28., 15.), Vec3::new(-38., -28., 15.), Vec3::new(38., -28., 15.)],
|
||||
4.,
|
||||
)
|
||||
.unwrap();
|
||||
let raw = solidcraft_kernel::boolean(&old_source, &cavity, solidcraft_kernel::BoolOp::Cut).unwrap().unwrap();
|
||||
let mut old_body = ModelBody::new("Case".into(), raw, shell);
|
||||
old_body.names = crate::naming::NameCell::new(Some(std::sync::Arc::new(crate::naming::NamingOrigin {
|
||||
feature: doc.feature(shell).unwrap().clone(),
|
||||
before,
|
||||
depth: 1,
|
||||
mats: vec![],
|
||||
sheets: vec![],
|
||||
})));
|
||||
// A point low on the outer round of the +x+y corner (radius 6 about (34, 24)), below the
|
||||
// cavity's floor at z = 2.
|
||||
let t = std::f64::consts::FRAC_1_SQRT_2;
|
||||
let at = Vec3::new(34. + 6. * t, 24. + 6. * t, 1.);
|
||||
let former = crate::naming::face_name_at_body(&old_body, at).unwrap();
|
||||
let stem = crate::naming::strip_piece(&former).to_string();
|
||||
assert_ne!(former, stem, "actual old cylindrical strip must have a piece suffix");
|
||||
let state = model.state();
|
||||
let current = state.body("Case").unwrap();
|
||||
let names = crate::naming::face_names(current);
|
||||
let stem = crate::naming::face_name_at_body(current, at).unwrap();
|
||||
assert_eq!(crate::naming::strip_piece(&stem), stem, "the shelled wall's round is one exact face, floor to rim");
|
||||
let index = names.iter().position(|name| name == &stem).unwrap();
|
||||
assert!(current.body.face_was_coalesced(index));
|
||||
assert!(current.body.face_plane(index).is_none(), "witness grants no planar attachment authority");
|
||||
assert!(old_body.body.face_sources().is_none_or(|sources| sources.iter().enumerate().all(|(index, _)| !old_body.body.face_was_coalesced(index))));
|
||||
let mut old_state = (*model.state()).clone();
|
||||
old_state.bodies = vec![old_body];
|
||||
// What a build that sewed the cut slab by slab saved: the round in two pieces split at the
|
||||
// cavity's floor, the projection made on the lower one, `#1` (pieces numbered up their
|
||||
// longest spread), from z = 0 to 2. Projected on YZ, the quarter round from (40, 24) to
|
||||
// (34, 30) is the rectangle y 24..30, z 0..2: its two arcs and its two straight sides.
|
||||
let former = format!("{stem}#1");
|
||||
let reference = former.clone();
|
||||
let source = LinkSource::Face { body: "Case".into(), at };
|
||||
// Recover the saved projection from the old face's complete raw topology.
|
||||
// Display seam suppression must not discard a historical strip boundary.
|
||||
let old_body = old_state.body("Case").unwrap();
|
||||
let old_index = crate::naming::face_on_body(old_body, &former, at).unwrap().0;
|
||||
let mesh = old_body.mesh();
|
||||
let mut geometry = Vec::new();
|
||||
for (index, faces) in mesh.edge_faces.iter().enumerate() {
|
||||
if !faces.contains(&(old_index as u32)) {
|
||||
continue;
|
||||
}
|
||||
let points = &mesh.edges[index];
|
||||
let a = solidcraft_geom::Plane::YZ.to_local(*points.first().unwrap());
|
||||
let b = solidcraft_geom::Plane::YZ.to_local(*points.last().unwrap());
|
||||
let (a, b) = (Vec2::new(a.x, a.y), Vec2::new(b.x, b.y));
|
||||
assert!(a.dist(b) > 1e-6);
|
||||
for point in points {
|
||||
let q = solidcraft_geom::Plane::YZ.to_local(*point);
|
||||
let q = Vec2::new(q.x, q.y);
|
||||
let direction = b - a;
|
||||
let t = ((q - a).dot(direction) / direction.dot(direction)).clamp(0., 1.);
|
||||
assert!(q.dist(a + direction * t) < 1e-7, "exact cylindrical quadrant projects to a straight support");
|
||||
}
|
||||
geometry.push(solidcraft_sketch::LinkGeom::Line(a, b));
|
||||
}
|
||||
assert_eq!(geometry.len(), 4, "historical curved strip has a complete four-edge rectangular projection");
|
||||
let (a, b, c, d) = (Vec2::new(24., 0.), Vec2::new(30., 0.), Vec2::new(30., 2.), Vec2::new(24., 2.));
|
||||
let geometry: Vec<_> = [(a, b), (b, c), (c, d), (d, a)].into_iter().map(|(p, q)| solidcraft_sketch::LinkGeom::Line(p, q)).collect();
|
||||
let mut sketch = Sketch::new();
|
||||
sketch.add_link(LinkKind::Project, source, &geometry).unwrap();
|
||||
sketch.links[0].face_name = Some(former);
|
||||
let original = sketch.clone();
|
||||
let sk = doc
|
||||
.add_feature(FeatureKind::Sketch { plane: PlaneRef::Custom { plane: solidcraft_geom::Plane::YZ }, sketch }, Some("Saved corner strip"))
|
||||
.unwrap();
|
||||
@@ -452,26 +378,21 @@ fn saved_curved_strip_projection_is_lost_after_exact_wall_coalescence() {
|
||||
let mut cold = Model::new();
|
||||
cold.evaluate(&loaded);
|
||||
for current in [&model, &cold] {
|
||||
// No face is the piece any more, and the whole wall is not it: the sketch fails,
|
||||
// asking for its face again, and so does the material made from it. The case builds.
|
||||
let result = current.results.iter().find(|r| r.id == sk).unwrap();
|
||||
assert!(result.error.is_none());
|
||||
assert!(result.warning.as_deref().unwrap().contains("no longer exists; last geometry retained"));
|
||||
assert_eq!(result.error.as_deref().and_then(crate::reselect_parts), Some(("Saved corner strip", reference.as_str())), "{result:?}");
|
||||
assert!(current.results.iter().find(|r| r.id == boss).unwrap().error.is_some());
|
||||
assert!(current.results.iter().filter(|r| r.id != sk && r.id != boss).all(|r| r.error.is_none()), "{:?}", current.results);
|
||||
let state = current.state();
|
||||
let solved = state.sketch(sk).unwrap();
|
||||
assert!(solved.sketch.links[0].lost);
|
||||
assert_eq!(solved.sketch.points, original.points);
|
||||
assert_eq!(solved.sketch.curves, original.curves);
|
||||
let material = state.bodies.iter().find(|body| body.feature == boss).unwrap();
|
||||
assert!(
|
||||
(solidcraft_kernel::measure(&material.body).unwrap().volume - 12.).abs() < 1e-7,
|
||||
"two-mm strip cannot expand to a full thirty-mm wall"
|
||||
);
|
||||
assert!(state.body("Case").is_some());
|
||||
assert!(state.bodies.iter().all(|body| body.feature != boss), "two-mm strip cannot expand to a full thirty-mm wall");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saved_stepped_tab_cut_piece_projection_is_lost_without_expanding_material() {
|
||||
use solidcraft_kernel::{BoolOp, FacePaint, Paint};
|
||||
fn saved_stepped_tab_cut_piece_projection_asks_for_its_face_again_without_expanding_material() {
|
||||
use solidcraft_sketch::{LinkKind, LinkSource, Sketch};
|
||||
let mut doc = Document::new("public stepped tab case");
|
||||
doc.set_param("length", "40", Some("mm"), None).unwrap();
|
||||
@@ -498,46 +419,26 @@ fn saved_stepped_tab_cut_piece_projection_is_lost_without_expanding_material() {
|
||||
)
|
||||
.unwrap();
|
||||
doc.feature_mut(tool).unwrap().body_names = vec!["Pocket tool".into()];
|
||||
let cut = doc
|
||||
.add_feature(
|
||||
FeatureKind::Combine { target: "Case".into(), tools: vec!["Pocket tool".into()], operation: crate::Operation::Cut, keep_tools: false },
|
||||
Some("Pocket"),
|
||||
)
|
||||
.unwrap();
|
||||
doc.add_feature(
|
||||
FeatureKind::Combine { target: "Case".into(), tools: vec!["Pocket tool".into()], operation: crate::Operation::Cut, keep_tools: false },
|
||||
Some("Pocket"),
|
||||
)
|
||||
.unwrap();
|
||||
let mut model = Model::new();
|
||||
model.evaluate(&doc);
|
||||
assert!(model.results.iter().all(|r| r.error.is_none()), "{:?}", model.results);
|
||||
let before = model.state_before(cut);
|
||||
// A real per-face paint control preserves the former slab construction;
|
||||
// no historical names or geometry are fabricated.
|
||||
let old_stock = before
|
||||
.body("Case")
|
||||
.unwrap()
|
||||
.body
|
||||
.clone()
|
||||
.with_paint(Some(Paint { opacity: 1., faces: vec![FacePaint { face: 0, color: [1., 0., 0.], opacity: 1. }] }));
|
||||
let old_cut = solidcraft_kernel::boolean(&old_stock, &before.body("Pocket tool").unwrap().body, BoolOp::Cut).unwrap().unwrap();
|
||||
let mut old_body = ModelBody::new("Case".into(), old_cut, cut);
|
||||
old_body.names = crate::naming::NameCell::new(Some(std::sync::Arc::new(crate::naming::NamingOrigin {
|
||||
feature: doc.feature(cut).unwrap().clone(),
|
||||
before,
|
||||
depth: 1,
|
||||
mats: vec![],
|
||||
sheets: vec![],
|
||||
})));
|
||||
// Older builds left Case's -x wall split by the Join and the Cut and saved a projection of
|
||||
// the patch y 10..20, z 0..20 by its piece name. The kernel leaves the wall one face now: the
|
||||
// patch is only that saved name and its rectangle on YZ.
|
||||
let at = Vec3::new(0., 15., 1.);
|
||||
let former = crate::naming::face_name_at_body(&old_body, at).unwrap();
|
||||
assert_ne!(former, crate::naming::strip_piece(&former), "actual former bounded wall patch has a full piece name");
|
||||
let index = crate::naming::face_on_body(&old_body, &former, at).unwrap().0;
|
||||
let mut old_state = crate::ModelState::default();
|
||||
old_state.bodies.push(old_body);
|
||||
let plane = solidcraft_geom::Plane::YZ;
|
||||
let resolved = crate::project::resolve_face_indices(&old_state, &plane, LinkKind::Project, "Case", &[index]).unwrap();
|
||||
let current_state = model.state();
|
||||
let current = current_state.body("Case").unwrap();
|
||||
let stem = crate::naming::strip_piece(&former);
|
||||
let stem = crate::naming::face_name_at_body(current, at).unwrap();
|
||||
assert_eq!(crate::naming::strip_piece(&stem), stem, "the -x wall is one face");
|
||||
let former = format!("{stem}#1");
|
||||
let current_names = crate::naming::face_names(current);
|
||||
let merged = current_names.iter().position(|name| name == stem).unwrap();
|
||||
let merged = current_names.iter().position(|name| name == &stem).unwrap();
|
||||
// The Join leaves Case's -x wall on its own support, so the wall keeps its exact box
|
||||
// identity through the Join and the Cut: one face again, no longer the saved piece.
|
||||
assert!(
|
||||
@@ -548,11 +449,12 @@ fn saved_stepped_tab_cut_piece_projection_is_lost_without_expanding_material() {
|
||||
"the merged wall carries its exact construction identity"
|
||||
);
|
||||
assert!(!current_names.contains(&former));
|
||||
let corners = [Vec2::new(10., 0.), Vec2::new(20., 0.), Vec2::new(20., 20.), Vec2::new(10., 20.)];
|
||||
let geometry: Vec<_> = (0..4).map(|i| solidcraft_sketch::LinkGeom::Line(corners[i], corners[(i + 1) % 4])).collect();
|
||||
let mut sketch = Sketch::new();
|
||||
sketch.add_link(LinkKind::Project, LinkSource::Face { body: "Case".into(), at }, &resolved.geom).unwrap();
|
||||
sketch.links[0].face_name = Some(former);
|
||||
let original = sketch.clone();
|
||||
let captured = original.points.iter().filter(|p| p.link.is_some()).map(|p| p.pos).collect::<Vec<_>>();
|
||||
sketch.add_link(LinkKind::Project, LinkSource::Face { body: "Case".into(), at }, &geometry).unwrap();
|
||||
sketch.links[0].face_name = Some(former.clone());
|
||||
let captured = sketch.points.iter().filter(|p| p.link.is_some()).map(|p| p.pos).collect::<Vec<_>>();
|
||||
assert_eq!(captured.len(), 4);
|
||||
assert!(captured.iter().all(|p| (p.x == 10. || p.x == 20.) && (p.y == 0. || p.y == 20.)));
|
||||
let sk = doc.add_feature(FeatureKind::Sketch { plane: PlaneRef::Custom { plane }, sketch }, Some("Saved wall patch")).unwrap();
|
||||
@@ -577,22 +479,13 @@ fn saved_stepped_tab_cut_piece_projection_is_lost_without_expanding_material() {
|
||||
let mut cold = Model::new();
|
||||
cold.evaluate(&reopened);
|
||||
for current in [&model, &cold] {
|
||||
// No face is the patch any more, and the whole wall is not it: the sketch fails,
|
||||
// asking for its face again, and so does the stock made from it.
|
||||
let result = current.results.iter().find(|r| r.id == sk).unwrap();
|
||||
assert!(result.error.is_none(), "{result:?}");
|
||||
assert!(result.warning.as_deref().is_some_and(|w| w.contains("no longer exists; last geometry retained")), "{result:?}");
|
||||
assert_eq!(result.error.as_deref().and_then(crate::reselect_parts), Some(("Saved wall patch", former.as_str())), "{result:?}");
|
||||
assert!(current.results.iter().find(|r| r.id == material).unwrap().error.is_some());
|
||||
let state = current.state();
|
||||
let saved = state.sketch(sk).unwrap();
|
||||
assert!(saved.sketch.links[0].lost);
|
||||
assert_eq!(saved.sketch.points, original.points);
|
||||
assert_eq!(saved.sketch.curves, original.curves);
|
||||
let stock = state.bodies.iter().find(|b| b.feature == material).unwrap();
|
||||
assert!(
|
||||
(solidcraft_kernel::measure(&stock.body).unwrap().volume - 200.).abs() < 1e-7,
|
||||
"saved10x20 patch must not become30x20 wall; actualvolume={}, bounds={:?}, originalpoints={:?}",
|
||||
solidcraft_kernel::measure(&stock.body).unwrap().volume,
|
||||
stock.mesh().bounds(),
|
||||
original.points
|
||||
);
|
||||
assert!(state.bodies.iter().all(|b| b.feature != material), "saved 10 x 20 patch must not become the 30 x 20 wall");
|
||||
let case = state.body("Case").unwrap();
|
||||
let expected = length * 30. * 20. + 8. * 10. * 8. - (length - 2. * wall) * (30. - 2. * wall) * (20. - wall);
|
||||
assert!((solidcraft_kernel::measure(&case.body).unwrap().volume - expected).abs() < 1e-6);
|
||||
|
||||
@@ -279,13 +279,13 @@ impl Document {
|
||||
let count = body.body.face_sources().map_or(0, |sources| sources.len());
|
||||
let mut indices: Vec<_> = (0..count).filter(|index| side(*index, true)).collect();
|
||||
// A side drawn in collinear pieces is one face over all of them (#1788): a certificate
|
||||
// made when the pieces were faces of their own finds it by the piece's name, which
|
||||
// the joined face answers to.
|
||||
// made when the pieces were faces of their own finds it by the piece's name, which is
|
||||
// a name of the joined face too (one of its synonyms).
|
||||
if indices.is_empty() {
|
||||
let names = crate::naming::names(body);
|
||||
indices = (0..count)
|
||||
.filter(|index| {
|
||||
side(*index, false) && crate::history_names::matches(&names, *index, &target.name) == Some(crate::history_names::Match::Alias)
|
||||
side(*index, false) && crate::history_names::matches(&names, *index, &target.name) == Some(crate::history_names::Match::Exact)
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
+176
-42
@@ -116,6 +116,11 @@ pub struct Construct {
|
||||
pub feature: u64,
|
||||
pub name: String,
|
||||
pub geom: ConstructGeom,
|
||||
/// The bodies it was built from, when every pick it reads lies on a body
|
||||
/// ([`source_bodies`]): a later Move of all of them carries it along (#10127). Empty when
|
||||
/// it also reads something fixed (a point, a plane, a sketch), which keeps it in place.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub bodies: Vec<String>,
|
||||
}
|
||||
|
||||
/// Everything a reference needs to be resolved.
|
||||
@@ -141,16 +146,16 @@ fn construct<'a>(st: &'a ModelState, name: &str) -> Result<&'a ConstructGeom> {
|
||||
st.construct.iter().find(|c| c.name == name).map(|c| &c.geom).ok_or_else(|| DocError::Unknown(format!("construction `{name}`")))
|
||||
}
|
||||
|
||||
/// The face picked: (body, face index, point on it).
|
||||
/// The face picked: (body, face index, point on it). A face picked with a name is the face the
|
||||
/// name finds, or the reference fails, asking for it to be picked again.
|
||||
fn face_of<'a>(st: &'a ModelState, at: Vec3, name: &Option<String>) -> Result<(&'a ModelBody, usize, Vec3)> {
|
||||
// The named face itself: its nearest point to the old pick can lie on an edge it shares
|
||||
// (a corner round that a parameter moved away from the pick), where picking again by the
|
||||
// point finds the flat face beside it (#1255).
|
||||
if let Some(hit) = name.as_deref().and_then(|n| crate::name_lookup::locate_face(st, n, at))
|
||||
&& let Some(b) = st.bodies.get(hit.body).filter(|b| !b.body.is_mesh())
|
||||
&& hit.face < b.body.face_count()
|
||||
{
|
||||
return Ok((b, hit.face, hit.point));
|
||||
if let Some(n) = name.as_deref() {
|
||||
return crate::name_lookup::locate_face(st, n, at)
|
||||
.and_then(|hit| st.bodies.get(hit.body).filter(|b| !b.body.is_mesh() && hit.face < b.body.face_count()).map(|b| (b, hit.face, hit.point)))
|
||||
.ok_or_else(|| DocError::reselect(n));
|
||||
}
|
||||
for b in bodies(st) {
|
||||
if let Some(fi) = crate::appearance::face_index_at(b, at) {
|
||||
@@ -165,16 +170,13 @@ fn edge_points(st: &ModelState, at: Vec3, name: &Option<String>) -> Result<Vec<V
|
||||
edge_pick(st, at, name).map(|(_, _, pts)| pts)
|
||||
}
|
||||
|
||||
/// The edge picked: its body, its index there (as `Body::edges` lists it) and its polyline.
|
||||
/// The edge picked: its body, its index there (as `Body::edges` lists it) and its polyline. An
|
||||
/// edge picked with a name is the edge the name finds, or the reference fails, asking for it to
|
||||
/// be picked again.
|
||||
fn edge_pick<'a>(st: &'a ModelState, at: Vec3, name: &Option<String>) -> Result<(&'a ModelBody, usize, Vec<Vec3>)> {
|
||||
let mut at = at;
|
||||
if let Some(n) = name {
|
||||
for b in bodies(st) {
|
||||
if let Ok((q, _)) = crate::naming::find_edge(b, n, at) {
|
||||
at = q;
|
||||
break;
|
||||
}
|
||||
}
|
||||
at = bodies(st).find_map(|b| crate::naming::find_edge(b, n, at).ok()).ok_or_else(|| DocError::reselect(n.as_str()))?;
|
||||
}
|
||||
let mut best: Option<(f64, &ModelBody, usize, Vec<Vec3>)> = None;
|
||||
for b in bodies(st) {
|
||||
@@ -437,31 +439,66 @@ fn centre(ctx: &Ctx, g: &GeoRef) -> Result<Vec3> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Point and unit tangent at fraction `t` along a polyline.
|
||||
fn along(pts: &[Vec3], t: f64) -> Option<(Vec3, Vec3)> {
|
||||
let total: f64 = pts.windows(2).map(|w| (w[1] - w[0]).len()).sum();
|
||||
if !(total > 0.0) {
|
||||
return None;
|
||||
}
|
||||
let mut left = t.clamp(0.0, 1.0) * total;
|
||||
for w in pts.windows(2) {
|
||||
let l = (w[1] - w[0]).len();
|
||||
if l <= 0.0 {
|
||||
continue;
|
||||
}
|
||||
if left <= l {
|
||||
return Some((w[0] + (w[1] - w[0]) * (left / l), (w[1] - w[0]) * (1.0 / l)));
|
||||
}
|
||||
left -= l;
|
||||
}
|
||||
let (a, b) = (pts.get(pts.len() - 2)?, pts.last()?);
|
||||
Some((*b, (*b - *a).normalized()?))
|
||||
/// The point a fraction `t` (0…1) of the way along a picked edge or sketch curve by arc length,
|
||||
/// and the unit direction of travel there ([`kernel::along_path`]): exact on the curve itself.
|
||||
fn along(ctx: &Ctx, g: &GeoRef, t: f64) -> Result<(Vec3, Vec3)> {
|
||||
Ok(kernel::along_path(&path_segs(ctx, g)?, t)?)
|
||||
}
|
||||
|
||||
fn path_points(ctx: &Ctx, g: &GeoRef) -> Result<Vec<Vec3>> {
|
||||
/// A picked edge or sketch curve as the path segments that are its curve exactly, from the end
|
||||
/// the drawn curve starts at: a body edge's record curve ([`kernel::edge_path`]), a planar sketch
|
||||
/// curve's exact pieces, a 3D sketch curve's arc, spline, line or linked exact curves. Only a
|
||||
/// curve that exists as points alone runs along them: an intersection edge (its record holds no
|
||||
/// exact curve, only the surfaces and a leader near it), a 3D curve known only as its points
|
||||
/// (projected onto a face, iso curves), a spline above the exact pieces' degree.
|
||||
fn path_segs(ctx: &Ctx, g: &GeoRef) -> Result<Vec<kernel::PathSeg>> {
|
||||
let lines = |pts: &[Vec3]| -> Vec<kernel::PathSeg> {
|
||||
pts.windows(2).filter(|w| w[0].dist(w[1]) > 0.0).map(|w| kernel::PathSeg::Line { a: w[0], b: w[1] }).collect()
|
||||
};
|
||||
match g {
|
||||
GeoRef::Edge { at, name } => edge_points(ctx.st, *at, name),
|
||||
GeoRef::SketchCurve { sketch, curve } => sketch_curve_points(ctx, *sketch, curve),
|
||||
GeoRef::Edge { at, name } => {
|
||||
let (b, i, pts) = edge_pick(ctx.st, *at, name)?;
|
||||
let procedural =
|
||||
b.body.record().edges.get(i).is_some_and(|e| matches!(e.parameters(), solidcraft_geom::brep::Curve::SurfaceIntersection { .. }));
|
||||
if procedural {
|
||||
return Ok(lines(&pts));
|
||||
}
|
||||
let segs = kernel::edge_path(&b.body, i)?;
|
||||
// The drawn edge sets which way t runs, as the picked edge's polyline has it.
|
||||
let (Some(first), Some(start)) = (pts.first(), segs.first().map(kernel::PathSeg::start)) else { return Ok(segs) };
|
||||
let end = segs.last().map(kernel::PathSeg::end).unwrap_or(start);
|
||||
Ok(if end.dist(*first) < start.dist(*first) { segs.iter().rev().map(kernel::PathSeg::reversed).collect() } else { segs })
|
||||
}
|
||||
GeoRef::SketchCurve { sketch, curve } => {
|
||||
let ss = ctx.st.sketch(*sketch).ok_or_else(|| DocError::Unknown(format!("sketch {sketch}")))?;
|
||||
// Evaluation has already mapped wires into this component's frame: their coordinates
|
||||
// are independent of the sketch plane.
|
||||
if let Some(wire) = ss.sketch.wires.iter().find(|wire| wire.id == *curve) {
|
||||
if let Some(arc) = &wire.arc {
|
||||
let angle = solidcraft_sketch::arc_sweep(arc);
|
||||
return Ok(vec![kernel::PathSeg::Arc { a: arc.start, center: arc.center, axis: arc.normal, angle }]);
|
||||
}
|
||||
if wire.fit.len() >= 3 {
|
||||
let spline = kernel::PathPiece::Spline { fit: wire.fit.clone(), reversed: false };
|
||||
if let Some(spans) = spline.spans()? {
|
||||
return Ok(spans);
|
||||
}
|
||||
}
|
||||
if let [a, b] = wire.fit.as_slice() {
|
||||
return Ok(vec![kernel::PathSeg::Line { a: *a, b: *b }]);
|
||||
}
|
||||
if !wire.exact.is_empty() {
|
||||
let mut segs = Vec::new();
|
||||
for c in &wire.exact {
|
||||
segs.extend(kernel::curve_path(&c.curve, c.interval)?);
|
||||
}
|
||||
return Ok(segs);
|
||||
}
|
||||
return Ok(lines(&wire.pts));
|
||||
}
|
||||
let ci = ss.sketch.curve_index(curve).ok_or_else(|| DocError::Unknown(format!("curve `{curve}`")))?;
|
||||
Ok(ss.sketch.exact_pieces(ci).iter().map(|s| kernel::PathSeg::of_sketch(&ss.plane, s)).collect())
|
||||
}
|
||||
_ => Err(bad("pick an edge or sketch curve as the path")),
|
||||
}
|
||||
}
|
||||
@@ -497,6 +534,48 @@ fn surface_normal(s: FaceSurface, p: Vec3) -> Option<(Vec3, Vec3)> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A point over `s` where the face turns towards `towards` (a reference plane's normal): around
|
||||
/// the axis of a cylinder, cone or torus, turned `angle` about it; straight at it on a sphere. Of
|
||||
/// the two sides, the one `at` is on (#8822).
|
||||
fn facing(s: FaceSurface, at: Vec3, towards: Vec3, angle: f64) -> Result<Vec3> {
|
||||
let side = |d: Vec3, w: Vec3| if d.dot(w) < 0.0 { d * -1.0 } else { d };
|
||||
let around = |axis: Vec3, w: Vec3| -> Result<(Vec3, f64)> {
|
||||
let d = (towards - axis * towards.dot(axis))
|
||||
.normalized()
|
||||
.ok_or_else(|| bad("the reference is square to the face's axis: every plane tangent to the face meets it at a right angle"))?;
|
||||
let d = d * angle.cos() + axis.cross(d) * angle.sin();
|
||||
let r = w - axis * w.dot(axis);
|
||||
Ok((side(d, r), r.len()))
|
||||
};
|
||||
if !angle.is_finite() {
|
||||
return Err(bad("the angle is not a number"));
|
||||
}
|
||||
Ok(match s {
|
||||
FaceSurface::Plane { .. } => at,
|
||||
FaceSurface::Cylinder { origin, axis, radius } => {
|
||||
let w = at - origin;
|
||||
let (d, _) = around(axis.into(), w)?;
|
||||
origin + axis * w.dot(axis) + d * radius
|
||||
}
|
||||
FaceSurface::Cone { apex, axis, .. } => {
|
||||
let w = at - apex;
|
||||
let (d, r) = around(axis.into(), w)?;
|
||||
apex + axis * w.dot(axis) + d * r.max(1e-6)
|
||||
}
|
||||
// On the outer equator, where the tube faces away from the axis.
|
||||
FaceSurface::Torus { center, axis, major, minor } => {
|
||||
let (d, _) = around(axis.into(), at - center)?;
|
||||
center + d * (major + minor)
|
||||
}
|
||||
FaceSurface::Sphere { center, radius } => {
|
||||
if angle.abs() > 1e-12 {
|
||||
return Err(bad("a sphere has no axis to turn the plane about: leave the angle out"));
|
||||
}
|
||||
center + side(towards, at - center) * radius
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn face_surface_at(ctx: &Ctx, g: &GeoRef) -> Result<(FaceSurface, Vec3)> {
|
||||
match g {
|
||||
GeoRef::Face { at, name } => {
|
||||
@@ -538,6 +617,46 @@ fn line_line(a: (Vec3, Vec3), b: (Vec3, Vec3)) -> Option<Vec3> {
|
||||
Some(((p + u * s) + (q + v * t)) * 0.5)
|
||||
}
|
||||
|
||||
/// The bodies a construction built from `refs` lies on: each face, edge and vertex pick's body,
|
||||
/// and the bodies of the constructions it is built from. Empty when any pick is not on a body
|
||||
/// (a fixed point, the origin, a plane, a world axis, a sketch) or is not found: such a
|
||||
/// construction is held by something a move of the bodies leaves where it is, so it stays.
|
||||
pub(crate) fn source_bodies(ctx: &Ctx, refs: &[&GeoRef]) -> Vec<String> {
|
||||
let mut out: Vec<String> = Vec::new();
|
||||
for g in refs {
|
||||
let found: Vec<String> = match g {
|
||||
GeoRef::Face { at, name } => face_of(ctx.st, *at, name).map(|(b, ..)| vec![b.definition_name().to_string()]).unwrap_or_default(),
|
||||
GeoRef::Edge { at, name } => edge_pick(ctx.st, *at, name).map(|(b, ..)| vec![b.definition_name().to_string()]).unwrap_or_default(),
|
||||
GeoRef::Vertex { .. } => {
|
||||
point(ctx, g).ok().and_then(|p| vertex_body(ctx.st, p)).map(|b| vec![b.definition_name().to_string()]).unwrap_or_default()
|
||||
}
|
||||
GeoRef::Axis { name } | GeoRef::ConstructPoint { name } => {
|
||||
ctx.st.construct.iter().find(|c| c.name.as_str() == AsRef::<str>::as_ref(name)).map(|c| c.bodies.clone()).unwrap_or_default()
|
||||
}
|
||||
_ => Vec::new(),
|
||||
};
|
||||
if found.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
for b in found {
|
||||
if !out.contains(&b) {
|
||||
out.push(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// The body with a vertex (an end of one of its edges) at `p`.
|
||||
fn vertex_body(st: &ModelState, p: Vec3) -> Option<&ModelBody> {
|
||||
bodies(st).find(|b| {
|
||||
let tol = (b.body.size() * 1e-3).max(1e-3);
|
||||
b.body
|
||||
.edges(tol)
|
||||
.is_ok_and(|edges| edges.iter().flat_map(|e| [e.points.first(), e.points.last()]).flatten().any(|v| v.dist(p) <= 1e-6 * (1.0 + p.len())))
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_axis(ctx: &Ctx, d: &AxisDef) -> Result<(Vec3, Vec3)> {
|
||||
match d {
|
||||
AxisDef::ThroughFace { face } => line(ctx, face),
|
||||
@@ -580,7 +699,7 @@ pub(crate) fn resolve_point(ctx: &Ctx, d: &PointDef) -> Result<Vec3> {
|
||||
}
|
||||
PointDef::AlongPath { path, t } => {
|
||||
let t = Document::eval_in(ctx.vals, t, Kind::Unitless)?;
|
||||
along(&path_points(ctx, path)?, t).map(|(p, _)| p).ok_or_else(|| bad("the path has no length"))
|
||||
along(ctx, path, t).map(|(p, _)| p)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -637,9 +756,17 @@ pub(crate) fn resolve_geo_plane(ctx: &Ctx, p: &PlaneRef) -> Result<Plane> {
|
||||
}
|
||||
Plane::new(pa, pb - pa, pc - pa).ok_or_else(degenerate)
|
||||
}
|
||||
PlaneRef::Tangent { face, at } => {
|
||||
PlaneRef::Tangent { face, at, reference, angle } => {
|
||||
let (s, _) = face_surface_at(ctx, face)?;
|
||||
let (on, n) = surface_normal(s, *at).ok_or_else(|| bad("the point is on the face's axis"))?;
|
||||
let at = match reference {
|
||||
Some(r) => {
|
||||
let towards = plane(ctx, r)?.normal().normalized().ok_or_else(degenerate)?;
|
||||
let angle = angle.as_ref().map(|a| Document::eval_in(ctx.vals, a, Kind::Angle)).transpose()?.unwrap_or(0.0);
|
||||
facing(s, *at, towards, angle)?
|
||||
}
|
||||
None => *at,
|
||||
};
|
||||
let (on, n) = surface_normal(s, at).ok_or_else(|| bad("the point is on the face's axis"))?;
|
||||
Plane::from_normal(on, n).ok_or_else(degenerate)
|
||||
}
|
||||
PlaneRef::Perpendicular { plane: base, line: l } => {
|
||||
@@ -653,7 +780,7 @@ pub(crate) fn resolve_geo_plane(ctx: &Ctx, p: &PlaneRef) -> Result<Plane> {
|
||||
}
|
||||
PlaneRef::AlongPath { path, t } => {
|
||||
let t = Document::eval_in(ctx.vals, t, Kind::Unitless)?;
|
||||
let (p, tan) = along(&path_points(ctx, path)?, t).ok_or_else(|| bad("the path has no length"))?;
|
||||
let (p, tan) = along(ctx, path, t)?;
|
||||
Plane::from_normal(p, tan).ok_or_else(degenerate)
|
||||
}
|
||||
PlaneRef::ThroughPoint { base, point: at } => {
|
||||
@@ -667,12 +794,15 @@ pub(crate) fn resolve_geo_plane(ctx: &Ctx, p: &PlaneRef) -> Result<Plane> {
|
||||
|
||||
impl GeoRef {
|
||||
/// Where a picked edge or face is now, found the way evaluation finds it (by its persistent
|
||||
/// name, else at the picked point): a point on it. `None` for other references and for an
|
||||
/// name when it has one, else at the picked point): a point on it. `None` for other references and for an
|
||||
/// edge or face that is gone.
|
||||
pub fn picked_point(&self, st: &ModelState) -> Option<Vec3> {
|
||||
match self {
|
||||
GeoRef::Edge { at, name } => {
|
||||
let on = name.as_deref().and_then(|n| bodies(st).find_map(|b| crate::naming::find_edge(b, n, *at).ok())).map_or(*at, |(q, _)| q);
|
||||
let on = match name.as_deref() {
|
||||
Some(n) => bodies(st).find_map(|b| crate::naming::find_edge(b, n, *at).ok())?,
|
||||
None => *at,
|
||||
};
|
||||
edge_pick(st, on, &None).ok().map(|_| on)
|
||||
}
|
||||
GeoRef::Face { at, name } => face_of(st, *at, name).ok().map(|(_, _, p)| p),
|
||||
@@ -781,7 +911,11 @@ impl PlaneRef {
|
||||
PlaneRef::Midplane { a, b } | PlaneRef::TwoEdges { a, b } | PlaneRef::AxisPoint { axis: a, point: b } => vec![a.as_mut(), b.as_mut()],
|
||||
PlaneRef::Perpendicular { plane, line } => vec![plane.as_mut(), line.as_mut()],
|
||||
PlaneRef::ThreePoints { a, b, c } => vec![a.as_mut(), b.as_mut(), c.as_mut()],
|
||||
PlaneRef::Tangent { face, .. } => vec![face.as_mut()],
|
||||
PlaneRef::Tangent { face, reference, .. } => {
|
||||
let mut v = vec![face.as_mut()];
|
||||
v.extend(reference.as_deref_mut());
|
||||
v
|
||||
}
|
||||
PlaneRef::AlongPath { path, .. } => vec![path.as_mut()],
|
||||
PlaneRef::ThroughPoint { base, point } => {
|
||||
let mut refs = base.refs_mut();
|
||||
|
||||
@@ -187,7 +187,13 @@ mod tests {
|
||||
}
|
||||
assert!(std::sync::Arc::ptr_eq(&reference, &mb.mesh()));
|
||||
assert_eq!(mb.display_plan_at(DisplayQuality::Normal).unwrap().vertex_count(), {
|
||||
let plan = mb.body.display_plan_angular((mb.body.size() * 1e-3).max(1e-3), solidcraft_kernel::DISPLAY_ANGLE).unwrap();
|
||||
let plan = mb
|
||||
.body
|
||||
.mesh_plan(&solidcraft_kernel::MeshTolerance {
|
||||
chord: (mb.body.size() * 1e-3).max(1e-3),
|
||||
angle: Some(solidcraft_kernel::DISPLAY_ANGLE),
|
||||
})
|
||||
.unwrap();
|
||||
plan.vertex_count()
|
||||
});
|
||||
}
|
||||
|
||||
+181
-29
@@ -15,21 +15,6 @@ pub const MAX_PARAMS: usize = 10_000;
|
||||
/// numbers, and far from overflowing the next id.
|
||||
pub const MAX_FEATURE_ID: u64 = 1 << 52;
|
||||
|
||||
/// Screw direction independent of the cylinder's placement and the picked end.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, solidcraft_derive::Walk)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ThreadHandedness {
|
||||
#[default]
|
||||
Right,
|
||||
Left,
|
||||
}
|
||||
|
||||
impl ThreadHandedness {
|
||||
pub fn is_right(&self) -> bool {
|
||||
*self == Self::Right
|
||||
}
|
||||
}
|
||||
|
||||
/// A named parameter. User parameters are created by the user; model parameters (`d1`, `d2`…)
|
||||
/// are created by dimensions and features.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@@ -101,8 +86,17 @@ pub enum PlaneRef {
|
||||
AxisPoint { axis: Box<GeoRef>, point: Box<GeoRef> },
|
||||
/// Through three points.
|
||||
ThreePoints { a: Box<GeoRef>, b: Box<GeoRef>, c: Box<GeoRef> },
|
||||
/// Tangent to a face (cylinder, cone, sphere, torus) where the point `at` lies over it.
|
||||
Tangent { face: Box<GeoRef>, at: Vec3 },
|
||||
/// Tangent to a face (cylinder, cone, sphere, torus) where the point `at` lies over it. With
|
||||
/// a `reference` (a plane or planar face), it touches where the face turns towards the
|
||||
/// reference instead, turned by `angle` about the face's axis, on the side of `at` (#8822).
|
||||
Tangent {
|
||||
face: Box<GeoRef>,
|
||||
at: Vec3,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
reference: Option<Box<GeoRef>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
angle: Option<Expr>,
|
||||
},
|
||||
/// Square to an edge or sketch curve, a fraction `t` (expression, 0…1) of the way along it.
|
||||
AlongPath { path: Box<GeoRef>, t: Expr },
|
||||
/// Through a straight edge (or line, axis) and square to a plane (or planar face).
|
||||
@@ -198,6 +192,33 @@ impl Operation {
|
||||
}
|
||||
}
|
||||
|
||||
/// What a join makes of pieces that share no face: they share no volume and no area, so nothing
|
||||
/// fuses them.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Touching {
|
||||
/// A piece that touches another only along an edge or at a point cannot be one solid with it
|
||||
/// and becomes a body of its own; pieces that touch nothing stay one body of several pieces.
|
||||
#[default]
|
||||
Separate,
|
||||
/// Every piece stays in one body, also those that touch along an edge or at a point.
|
||||
OneBody,
|
||||
}
|
||||
|
||||
impl Touching {
|
||||
pub fn parse(s: &str) -> Option<Touching> {
|
||||
Some(match s.to_ascii_lowercase().replace([' ', '-'], "_").as_str() {
|
||||
"separate" => Touching::Separate,
|
||||
"one_body" => Touching::OneBody,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
fn is_separate(&self) -> bool {
|
||||
*self == Touching::Separate
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Direction {
|
||||
@@ -303,6 +324,13 @@ pub struct Extent {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[walk(angle = "Taper angle")]
|
||||
pub taper: Option<Expr>,
|
||||
/// How a taper closes the profile's sharp corners, when the user chose
|
||||
/// ([`solidcraft_kernel::draft_corners`] says what each rule gives at each corner). Without
|
||||
/// it each profile takes preserve curvature where the kernel can build it exactly and the
|
||||
/// tangent rule where a curve that is neither a line nor an arc has a corner, and the
|
||||
/// feature's warning says so.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub corner: Option<solidcraft_kernel::DraftCorner>,
|
||||
/// To Object: end at the face (or vertex/point) at this point, instead of `distance`.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub to: Option<FacePick>,
|
||||
@@ -314,6 +342,11 @@ pub struct Extent {
|
||||
/// `start_offset`).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub from: Option<FacePick>,
|
||||
/// A Join To Object leaves the body it runs to as its own body, joining only its targets.
|
||||
/// Designs saved before a Join also took that body (format 39 and older, #8151) open with
|
||||
/// it set, so they keep the bodies they were built with.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
pub to_body_apart: bool,
|
||||
}
|
||||
|
||||
/// Thin Extrude: a wall of constant thickness along sketch curves instead of the profiles.
|
||||
@@ -651,6 +684,14 @@ pub enum FeatureKind {
|
||||
/// (default) or a G2 patch repeating each blend up to the second order.
|
||||
#[serde(default, skip_serializing_if = "FilletCorner::is_rolling")]
|
||||
corner: FilletCorner,
|
||||
/// Corners set back: each corner's patch starts its distance along every edge there
|
||||
/// from the vertex instead of where the rounds meet (a constant-radius fillet only).
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
setbacks: Vec<CornerSetback>,
|
||||
/// Round each picked edge's tangent chain with it (the default: edges continuing it
|
||||
/// smoothly at its ends, as far as they go), or only the picked edges.
|
||||
#[serde(default = "yes", skip_serializing_if = "is_yes")]
|
||||
tangent_chain: bool,
|
||||
},
|
||||
Chamfer {
|
||||
edges: Vec<Position>,
|
||||
@@ -669,6 +710,9 @@ pub enum FeatureKind {
|
||||
flip: bool,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
body: Option<BodyName>,
|
||||
/// Cut each picked edge's tangent chain with it (the default), or only the picked edges.
|
||||
#[serde(default = "yes", skip_serializing_if = "is_yes")]
|
||||
tangent_chain: bool,
|
||||
},
|
||||
Box {
|
||||
#[serde(with = "point_expr")]
|
||||
@@ -805,9 +849,17 @@ pub enum FeatureKind {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[walk(length = "Thread depth")]
|
||||
thread_depth: Option<Expr>,
|
||||
/// Screw direction of the cosmetic thread; older holes are right-handed.
|
||||
#[serde(default, skip_serializing_if = "ThreadHandedness::is_right")]
|
||||
handedness: ThreadHandedness,
|
||||
/// The tapped thread's hand: right (the default) or left; never both.
|
||||
#[serde(default, skip_serializing_if = "crate::thread_forms::is_default")]
|
||||
hand: crate::thread_forms::ThreadHand,
|
||||
/// The tapped thread cut into the wall (the groove between its teeth, as a modelled
|
||||
/// `solid.thread` cuts it) instead of only listed.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
modeled: bool,
|
||||
/// A modelled tapped thread's inner end, towards the bottom: it fades out over this
|
||||
/// length (ISO 3508's runout); none: it stops square, or clear of a blind hole's bottom.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
ease_out: Option<ThreadRunout>,
|
||||
/// To Object: drill down to the face (or point) here instead of `depth`.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
to: Option<FacePick>,
|
||||
@@ -824,14 +876,43 @@ pub enum FeatureKind {
|
||||
face: FacePick,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
designation: Option<String>,
|
||||
/// Thread length from the face's end nearest `face`; `None` = full length.
|
||||
/// Thread length from the end it starts at; `None` = full length (or `up_to`).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[walk(length = "Length")]
|
||||
length: Option<Expr>,
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
modeled: bool,
|
||||
#[serde(default, skip_serializing_if = "ThreadHandedness::is_right")]
|
||||
handedness: ThreadHandedness,
|
||||
/// A point near the end of the face the thread starts at; `None`: the face's only free
|
||||
/// end (no material past it), or the end the pick at `face` lies near.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
from: Option<Position>,
|
||||
/// Where the thread runs up to from the end it starts at, instead of a length.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
up_to: Option<ThreadLimit>,
|
||||
/// The thread's form; `designation`'s prefix names it when this is the default.
|
||||
#[serde(default, skip_serializing_if = "crate::thread_forms::is_default")]
|
||||
form: crate::thread_forms::ThreadForm,
|
||||
#[serde(default, skip_serializing_if = "crate::thread_forms::is_default")]
|
||||
hand: crate::thread_forms::ThreadHand,
|
||||
/// Starts (1 to 8): the lead is starts × pitch (unless `designation` gives the lead).
|
||||
#[serde(default = "crate::thread_forms::one_start", skip_serializing_if = "crate::thread_forms::is_one_start")]
|
||||
starts: u32,
|
||||
/// The runout at the thread's inner end, the one away from where it starts (a groove
|
||||
/// that fades out); none: the groove stops square there.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
ease_out: Option<ThreadRunout>,
|
||||
/// The chamfered tip at the thread's free end (ISO 4753).
|
||||
#[serde(default, skip_serializing_if = "crate::thread_forms::is_default")]
|
||||
tip: crate::thread_forms::ThreadTip,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[walk(angle = "Chamfer angle")]
|
||||
chamfer_angle: Option<Expr>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[walk(length = "Chamfer depth")]
|
||||
chamfer_depth: Option<Expr>,
|
||||
/// Buttress forms: which way the steep flank of the screw's teeth faces.
|
||||
#[serde(default, skip_serializing_if = "crate::thread_forms::is_default")]
|
||||
steep_flank: crate::thread_forms::SteepFlank,
|
||||
},
|
||||
/// Ruled loft through profiles of several sketches, in order.
|
||||
Loft {
|
||||
@@ -1208,9 +1289,10 @@ pub enum FeatureKind {
|
||||
body: Option<BodyName>,
|
||||
},
|
||||
/// A perforation field (#732): round holes of `diameter` on a staggered (60°) or square grid of `pitch` over the
|
||||
/// whole flat sheet, kept `margin` (default: the diameter) clear of its edges, cut-outs and bend zones. Stored like
|
||||
/// a cosmetic thread: the holes go to the flat pattern, the DXF and the measured volume, not into the B-rep, so a
|
||||
/// panel can carry tens of thousands of them.
|
||||
/// whole flat sheet, or inside the closed profiles of `sketch` when it is given, kept `margin` (default: the
|
||||
/// diameter) clear of its edges, cut-outs, bend zones and the outline. Stored like a cosmetic thread: the holes go
|
||||
/// to the flat pattern, the DXF and the measured volume, not into the B-rep, so a panel can carry tens of thousands
|
||||
/// of them.
|
||||
SheetPerforate {
|
||||
#[walk(length = "Diameter")]
|
||||
diameter: Expr,
|
||||
@@ -1223,6 +1305,13 @@ pub enum FeatureKind {
|
||||
pattern: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
body: Option<BodyName>,
|
||||
/// The field's outline: a sketch on a face of the sheet. None: the whole sheet.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
sketch: Option<SketchId>,
|
||||
/// Which of the outline sketch's profiles bound the field. None: all of them.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[walk(profiles_of = sketch)]
|
||||
profiles: Option<ProfileSel>,
|
||||
},
|
||||
/// Fold a sheet along a line on its base face: a sketch line (followed when the sketch
|
||||
/// changes), else the points `a`, `b`.
|
||||
@@ -1580,7 +1669,9 @@ pub enum FeatureKind {
|
||||
/// A strip of ruled surface `distance` wide hanging off the chain of `body`'s edges through
|
||||
/// `edges`, as a new surface body: along `mode` (`angle` from `direction`, or from the
|
||||
/// face's normal; along `direction`), from the face at `face` where it borders the chain;
|
||||
/// `flip` puts it on the other side; sharp corners as `corners` says.
|
||||
/// `flip` puts it on the other side; sharp corners as `corners` says. `far`: per chain edge,
|
||||
/// the interior control points of a free strip's far curve (moved handles); `None` or
|
||||
/// missing, the kernel's default curve.
|
||||
SurfaceRuled {
|
||||
body: BodyName,
|
||||
edges: Vec<Position>,
|
||||
@@ -1599,6 +1690,8 @@ pub enum FeatureKind {
|
||||
corners: RuledCorners,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
face: Option<FacePick>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
far: Vec<Option<Vec<Position>>>,
|
||||
},
|
||||
/// A surface body's boundary edges through `edges` extended outward: by `distance`, or up
|
||||
/// to `up_to`; the surface continued (`mode` natural) or a run-off face along each edge
|
||||
@@ -1774,6 +1867,26 @@ pub enum FeatureKind {
|
||||
file: String,
|
||||
meshes: Vec<MeshData>,
|
||||
},
|
||||
/// Repair Part (#8371): faces SMLib refuses rebuilt on their own surfaces, free edges that
|
||||
/// pair up within `gap` sewn, and, when `patch_holes`, the loops of free edges left patched
|
||||
/// and sewn in (`kernel::repair`).
|
||||
Repair {
|
||||
body: BodyName,
|
||||
#[walk(length = "Gap")]
|
||||
gap: Expr,
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
patch_holes: bool,
|
||||
},
|
||||
}
|
||||
|
||||
/// A fillet corner set back: the vertex at `vertex`, where every edge is rounded, has its corner
|
||||
/// patch start `distance` along each of its edges from the vertex, the rounds running whole up to
|
||||
/// their sections there. One distance for all of a corner's edges.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, solidcraft_derive::Walk)]
|
||||
pub struct CornerSetback {
|
||||
pub vertex: Position,
|
||||
#[walk(length = "Setback")]
|
||||
pub distance: Expr,
|
||||
}
|
||||
|
||||
/// What fills the corner where three blended edges meet.
|
||||
@@ -1783,7 +1896,9 @@ pub enum FilletCorner {
|
||||
/// A piece of the rolling ball's sphere: tangent (G1) to each blend.
|
||||
#[default]
|
||||
Rolling,
|
||||
/// A G2 patch repeating each blend's surface up to the second order (see `corner_g2`).
|
||||
/// The corner set back (its distance from the fillet's `setbacks`, else 1.5 × the radius)
|
||||
/// and filled with a patch meeting the rounds and the faces round it to the second order
|
||||
/// (`solidcraft_kernel::fillet_g2_corners`).
|
||||
G2,
|
||||
}
|
||||
|
||||
@@ -1812,6 +1927,29 @@ impl ExtendMode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Where a thread runs up to instead of a length.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, solidcraft_derive::Walk)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ThreadLimit {
|
||||
/// Where this point lies along the thread's axis (a point on a face, such as a shoulder).
|
||||
Face(FacePick),
|
||||
/// Where this plane crosses the thread's axis.
|
||||
Plane(PlaneRef),
|
||||
}
|
||||
|
||||
/// A thread's runout length at one end: a length, or ISO 3508's standard (2.5 P) or short
|
||||
/// (1.25 P) runout.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, solidcraft_derive::Walk)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ThreadRunout {
|
||||
Standard,
|
||||
Short,
|
||||
Length {
|
||||
#[walk(length = "Runout")]
|
||||
length: Expr,
|
||||
},
|
||||
}
|
||||
|
||||
/// What Extend extends up to instead of a distance.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, solidcraft_derive::Walk)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@@ -2223,7 +2361,11 @@ pub enum PatternKind {
|
||||
reversed2: bool,
|
||||
},
|
||||
Circular {
|
||||
origin: Position,
|
||||
/// A point on the axis the copies turn about: numbers, or length expressions it follows
|
||||
/// as the parameters change (#10852).
|
||||
#[serde(with = "point_expr")]
|
||||
#[walk(placement)]
|
||||
origin: PointExpr,
|
||||
axis: Dir,
|
||||
#[walk(unitless = "Quantity")]
|
||||
count: Expr,
|
||||
@@ -2431,6 +2573,7 @@ impl FeatureKind {
|
||||
FeatureKind::Import { .. } => "BaseFeature",
|
||||
FeatureKind::MeshImport { .. } => "MeshFeature",
|
||||
FeatureKind::MeshConvert { .. } => "MeshConvertFeature",
|
||||
FeatureKind::Repair { .. } => "RepairFeature",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2511,6 +2654,7 @@ impl FeatureKind {
|
||||
FeatureKind::Import { .. } => "import",
|
||||
FeatureKind::MeshConvert { .. } => "mesh_convert",
|
||||
FeatureKind::MeshImport { .. } => "mesh_import",
|
||||
FeatureKind::Repair { .. } => "repair",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2591,6 +2735,7 @@ impl FeatureKind {
|
||||
"BaseFeature" => "import",
|
||||
"MeshFeature" => "mesh_import",
|
||||
"MeshConvertFeature" => "mesh_convert",
|
||||
"RepairFeature" => "repair",
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
@@ -2675,6 +2820,7 @@ impl FeatureKind {
|
||||
FeatureKind::Import { .. } => "Import",
|
||||
FeatureKind::MeshImport { .. } => "Mesh",
|
||||
FeatureKind::MeshConvert { .. } => "ConvertMesh",
|
||||
FeatureKind::Repair { .. } => "Repair",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2733,6 +2879,11 @@ pub struct Feature {
|
||||
/// its faces are named after them and references follow them through Update Links (#3330).
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub link_faces: Vec<Vec<String>>,
|
||||
/// What a join of this feature makes of pieces that share no face: those that touch along an
|
||||
/// edge or at a point become separate bodies and those that touch nothing stay one body (the
|
||||
/// default), or every piece stays one body.
|
||||
#[serde(default, skip_serializing_if = "Touching::is_separate")]
|
||||
pub touching: Touching,
|
||||
#[serde(flatten)]
|
||||
pub kind: FeatureKind,
|
||||
}
|
||||
@@ -3792,6 +3943,7 @@ impl Document {
|
||||
kernel_piece_order: false,
|
||||
inserted: false,
|
||||
link_faces: Vec::new(),
|
||||
touching: Touching::Separate,
|
||||
kind,
|
||||
};
|
||||
self.place_feature(f);
|
||||
|
||||
+1413
-1871
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,9 @@ use crate::eval::{PlacementReads, body_world, hash_placement, placement_digest};
|
||||
use crate::*;
|
||||
|
||||
/// Work counted on this thread: bodies moved, STEP texts digested, joint faces and bodies
|
||||
/// measured, bodies searched for by name, features searched for by id.
|
||||
/// measured, bodies searched for by name, features searched for by id, bodies a cut (or
|
||||
/// intersect) ran its booleans on, bodies searched for the cylinder a tap marking lies on, and
|
||||
/// bodies whose faces a tapped hole read for its wall.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
pub(super) struct Counts {
|
||||
pub moved: usize,
|
||||
@@ -25,11 +27,25 @@ pub(super) struct Counts {
|
||||
pub measures: usize,
|
||||
pub body_searches: usize,
|
||||
pub feature_searches: usize,
|
||||
pub cut: usize,
|
||||
pub cylinder_search: usize,
|
||||
pub tapped_wall: usize,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static COUNTS: Cell<Counts> =
|
||||
const { Cell::new(Counts { moved: 0, digested: 0, joint_faces: 0, measures: 0, body_searches: 0, feature_searches: 0 }) };
|
||||
static COUNTS: Cell<Counts> = const {
|
||||
Cell::new(Counts {
|
||||
moved: 0,
|
||||
digested: 0,
|
||||
joint_faces: 0,
|
||||
measures: 0,
|
||||
body_searches: 0,
|
||||
feature_searches: 0,
|
||||
cut: 0,
|
||||
cylinder_search: 0,
|
||||
tapped_wall: 0,
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
pub(super) fn count(f: impl FnOnce(&mut Counts)) {
|
||||
@@ -298,7 +314,7 @@ fn sketch_on_a_turned_occurrence() -> (Document, Model, u64, u64, FaceAnchor, Pl
|
||||
let state = model.state();
|
||||
let raw = state.bodies.first().unwrap();
|
||||
let index = raw.body.face_sources().unwrap().iter().position(|s| matches!(s, Some(kernel::FaceSource::Box { axis: 2, high: true, .. }))).unwrap();
|
||||
let world = world_state(&doc, &state);
|
||||
let world = world_state(&doc, &state).unwrap();
|
||||
let shown = world.bodies.first().unwrap();
|
||||
let picked = shown.body.face_plane(index).unwrap();
|
||||
let anchor = doc.capture_face_anchor(raw, index, &picked, &path, 0, &[]).unwrap();
|
||||
@@ -676,9 +692,9 @@ fn placed_bodies_taken_as_before_keep_their_measure() {
|
||||
let mut state = ModelState::default();
|
||||
state.bodies.push(ModelBody::new("Pin".into(), kernel::cylinder(Vec3::ZERO, Vec3::Z, 2.0, 8.0).unwrap(), 1));
|
||||
let mut cache = PlacedBodies::default();
|
||||
let before = world_state_reusing(&doc, &state, &mut cache);
|
||||
let before = world_state_reusing(&doc, &state, &mut cache).unwrap();
|
||||
let centroid = before.bodies[0].measure().unwrap().centroid;
|
||||
let again = world_state_reusing(&doc, &state, &mut cache);
|
||||
let again = world_state_reusing(&doc, &state, &mut cache).unwrap();
|
||||
assert_eq!(counted(|| assert_eq!(again.bodies[0].measure().unwrap().centroid, centroid)).measures, 0);
|
||||
}
|
||||
|
||||
@@ -1045,11 +1061,13 @@ fn placed_parts(comps: usize, bodies: usize, sketches: usize, taps: usize, copie
|
||||
radius: 3.0,
|
||||
pitch: 1.0,
|
||||
internal: true,
|
||||
handedness: Default::default(),
|
||||
axis_point: Vec3::new(1.0, 1.0, 0.0),
|
||||
axis: solidcraft_geom::UnitVec3::Z,
|
||||
start: 0.0,
|
||||
end: 4.0,
|
||||
form: Default::default(),
|
||||
hand: Default::default(),
|
||||
starts: 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1063,7 +1081,7 @@ fn placing_parts_looks_each_sketch_and_tap_up_once() {
|
||||
let searches = |sketches: usize, taps: usize| {
|
||||
let (doc, state) = placed_parts(4, 2, sketches, taps, 3);
|
||||
let mut world = ModelState::default();
|
||||
let n = searched(|| world = world_state(&doc, &state));
|
||||
let n = searched(|| world = world_state(&doc, &state).unwrap());
|
||||
assert_eq!(world.bodies.len(), 4 * 2 * 3);
|
||||
assert_eq!(world.sketches.len(), 4 * sketches);
|
||||
assert_eq!(world.threads.len(), 4 * taps * 3);
|
||||
@@ -1077,9 +1095,390 @@ fn a_placed_tap_follows_its_body_offset() {
|
||||
let (mut doc, state) = placed_parts(1, 1, 0, 1, 1);
|
||||
let comp = doc.components[0].id;
|
||||
doc.body_offsets.insert("B0_0".into(), rigid(Vec3::new(0.0, 0.0, 7.0), Vec3::ZERO, Vec3::Z, 0.0));
|
||||
let world = world_state(&doc, &state);
|
||||
let world = world_state(&doc, &state).unwrap();
|
||||
let tap = world.threads.first().unwrap();
|
||||
let expected =
|
||||
apply_point(&mat_mul(&doc.component_transform(comp), &rigid(Vec3::new(0.0, 0.0, 7.0), Vec3::ZERO, Vec3::Z, 0.0)), Vec3::new(1.0, 1.0, 0.0));
|
||||
assert!(tap.axis_point.dist(expected) < 1e-9, "{:?} {expected:?}", tap.axis_point);
|
||||
}
|
||||
|
||||
/// What adding a feature of `kind` at the end of `doc` did to the bodies already there: how many
|
||||
/// its cut ran booleans on, and which it left exactly as they were (the same geometry, its name,
|
||||
/// key and face names carried over). A cut that cannot reach a body hands it back as it was, and
|
||||
/// must not pay a whole boolean to find that out (#10225); the sheet metal hole (#10226) and the
|
||||
/// cosmetic thread (#10227) check their own shortcuts with it too.
|
||||
pub(super) struct CutWork {
|
||||
pub cut: usize,
|
||||
pub kept: Vec<String>,
|
||||
pub model: Model,
|
||||
}
|
||||
|
||||
pub(super) fn cut_work(doc: &mut Document, kind: FeatureKind) -> CutWork {
|
||||
let mut model = Model::new();
|
||||
model.evaluate(doc);
|
||||
let id = doc.add_feature(kind, None).unwrap();
|
||||
let work = counted(|| model.evaluate(doc));
|
||||
let result = model.result(id).unwrap();
|
||||
assert_eq!(result.error, None, "the feature builds");
|
||||
let before = model.state_before(id);
|
||||
let kept = result
|
||||
.state
|
||||
.bodies
|
||||
.iter()
|
||||
.filter(|b| {
|
||||
before
|
||||
.body(&b.name)
|
||||
.is_some_and(|old| old.body.same(&b.body) && old.key == b.key && crate::naming::face_names(old) == crate::naming::face_names(b))
|
||||
})
|
||||
.map(|b| b.name.clone())
|
||||
.collect();
|
||||
CutWork { cut: work.cut, kept, model }
|
||||
}
|
||||
|
||||
/// A box body named `name`, `size` long on each side from `corner`.
|
||||
fn block(doc: &mut Document, name: &str, corner: Vec3, size: [f64; 3]) {
|
||||
let id = doc
|
||||
.add_feature(
|
||||
FeatureKind::Box {
|
||||
corner: point_expr(corner),
|
||||
length: size[0].to_string().into(),
|
||||
width: size[1].to_string().into(),
|
||||
height: size[2].to_string().into(),
|
||||
operation: Operation::NewBody,
|
||||
},
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
doc.feature_mut(id).unwrap().body_names = vec![name.into()];
|
||||
}
|
||||
|
||||
/// A cylinder that cuts, standing on `base` along Z.
|
||||
fn cutter(base: Vec3, radius: f64, height: f64) -> FeatureKind {
|
||||
FeatureKind::Cylinder {
|
||||
base: point_expr(base),
|
||||
axis: Dir(Vec3::Z),
|
||||
radius: radius.to_string().into(),
|
||||
height: height.to_string().into(),
|
||||
operation: Operation::Cut,
|
||||
references: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A cut leaves the bodies its tool cannot reach as they were, without a boolean on each: in a
|
||||
/// design of many bodies those booleans were most of a hole's time (#10225).
|
||||
#[test]
|
||||
fn a_cut_leaves_bodies_it_cannot_reach_as_they_were() {
|
||||
let mut doc = Document::new("far");
|
||||
block(&mut doc, "Near", Vec3::ZERO, [50.0, 40.0, 30.0]);
|
||||
// 0.5 mm clear of the tool, and far off.
|
||||
block(&mut doc, "Beside", Vec3::new(13.5, 0.0, 0.0), [20.0, 40.0, 30.0]);
|
||||
for i in 0..5 {
|
||||
block(&mut doc, &format!("Far{i}"), Vec3::new(200.0 * (i + 1) as f64, 0.0, 0.0), [50.0, 40.0, 30.0]);
|
||||
}
|
||||
// Near and Beside overlap; the hole goes through Near only, beside Beside.
|
||||
let work = cut_work(&mut doc, cutter(Vec3::new(10.0, 20.0, -5.0), 3.0, 40.0));
|
||||
assert_eq!(work.cut, 1, "only the body the tool reaches goes through the boolean");
|
||||
assert_eq!(work.kept, ["Beside", "Far0", "Far1", "Far2", "Far3", "Far4"]);
|
||||
assert!(volume(&work.model, "Near") < 60_000.0 - 800.0, "and it is cut");
|
||||
}
|
||||
|
||||
/// A cut still goes through the boolean wherever its tool reaches a body: on a face, along a
|
||||
/// line, into a dome above its edge circle. The boxes are exact, so a tool clear of a body by
|
||||
/// more than their tolerances leaves it out. (Bodies its tool cannot reach are left out of it:
|
||||
/// `a_cut_leaves_bodies_it_cannot_reach_as_they_were`.)
|
||||
#[test]
|
||||
fn a_cut_still_runs_where_its_tool_touches_grazes_or_meets_a_curved_face() {
|
||||
// A cylinder whose side is tangent to the face x = 50, one 0.5 µm clear of it (far past the
|
||||
// bodies' tolerances, so left out), one 0.1 mm in.
|
||||
for (x, cuts, boolean) in [(53.0, false, 1), (53.0005, false, 0), (52.9, true, 1)] {
|
||||
let mut doc = Document::new("graze");
|
||||
block(&mut doc, "A", Vec3::ZERO, [50.0, 40.0, 30.0]);
|
||||
let work = cut_work(&mut doc, cutter(Vec3::new(x, 20.0, 0.0), 3.0, 30.0));
|
||||
assert_eq!(work.cut, boolean, "the tool at x = {x} goes through the boolean {boolean} time(s)");
|
||||
let v = volume(&work.model, "A");
|
||||
assert_eq!(v < 60_000.0 - 1.0, cuts, "the tool at x = {x} cuts material: {v}");
|
||||
assert!(!cuts || work.kept.is_empty(), "a body cut is a new body");
|
||||
}
|
||||
// A box meeting the block only along its top edge.
|
||||
let mut doc = Document::new("edge");
|
||||
block(&mut doc, "A", Vec3::ZERO, [50.0, 40.0, 30.0]);
|
||||
let along = FeatureKind::Box {
|
||||
corner: point_expr(Vec3::new(50.0, 0.0, 30.0)),
|
||||
length: "10".into(),
|
||||
width: "40".into(),
|
||||
height: "10".into(),
|
||||
operation: Operation::Cut,
|
||||
};
|
||||
assert_eq!(cut_work(&mut doc, along).cut, 1);
|
||||
// A dome: a sphere's cap rising 10 mm above the circle it stands on. A thin tool drilled into
|
||||
// its top near the apex, well above that circle, cuts it.
|
||||
let mut doc = Document::new("dome");
|
||||
doc.add_feature(FeatureKind::Sphere { center: point_expr(Vec3::new(0.0, 0.0, -10.0)), radius: "20".into(), operation: Operation::NewBody }, None)
|
||||
.unwrap();
|
||||
let below = FeatureKind::Box {
|
||||
corner: point_expr(Vec3::new(-30.0, -30.0, -40.0)),
|
||||
length: "60".into(),
|
||||
width: "60".into(),
|
||||
height: "40".into(),
|
||||
operation: Operation::Cut,
|
||||
};
|
||||
doc.add_feature(below, None).unwrap();
|
||||
let mut model = Model::new();
|
||||
model.evaluate(&doc);
|
||||
let dome = model.state().bodies[0].name.clone();
|
||||
let whole = volume(&model, &dome);
|
||||
let work = cut_work(&mut doc, cutter(Vec3::new(2.0, 1.0, 8.0), 1.0, 5.0));
|
||||
assert_eq!(work.cut, 1);
|
||||
assert!(volume(&work.model, &dome) < whole - 1.0, "the drill cuts into the dome");
|
||||
assert!(work.kept.is_empty());
|
||||
}
|
||||
|
||||
/// A cylinder body named `name` standing on `base` along Z.
|
||||
fn shaft(doc: &mut Document, name: &str, base: Vec3, radius: f64, height: f64) {
|
||||
let id = doc
|
||||
.add_feature(
|
||||
FeatureKind::Cylinder {
|
||||
base: point_expr(base),
|
||||
axis: Dir(Vec3::Z),
|
||||
radius: radius.to_string().into(),
|
||||
height: height.to_string().into(),
|
||||
operation: Operation::NewBody,
|
||||
references: Vec::new(),
|
||||
},
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
doc.feature_mut(id).unwrap().body_names = vec![name.into()];
|
||||
}
|
||||
|
||||
/// The work of adding `kind` at the end of `doc`, and the tap markings the design then has.
|
||||
fn marking_work(doc: &mut Document, kind: FeatureKind) -> (Counts, Vec<ThreadInfo>) {
|
||||
let mut model = Model::new();
|
||||
model.evaluate(doc);
|
||||
let id = doc.add_feature(kind, None).unwrap();
|
||||
let work = counted(|| model.evaluate(doc));
|
||||
assert_eq!(model.result(id).unwrap().error, None, "the feature builds");
|
||||
(work, model.state().threads.clone())
|
||||
}
|
||||
|
||||
/// A cosmetic tap marking at `p`.
|
||||
fn marking(p: Vec3) -> FeatureKind {
|
||||
FeatureKind::Thread {
|
||||
face: FacePick(p),
|
||||
designation: Some("M6".into()),
|
||||
length: None,
|
||||
modeled: false,
|
||||
from: None,
|
||||
up_to: None,
|
||||
form: Default::default(),
|
||||
hand: crate::thread_forms::ThreadHand::Right,
|
||||
starts: 1,
|
||||
ease_out: None,
|
||||
tip: Default::default(),
|
||||
chamfer_angle: None,
|
||||
chamfer_depth: None,
|
||||
steep_flank: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A tap marking looks for its cylinder in the bodies near it only: each body searched was
|
||||
/// tessellated again, so many markings in a design of many bodies took quadratic time (#10227).
|
||||
#[test]
|
||||
fn a_tap_marking_searches_only_the_bodies_near_it() {
|
||||
let mut doc = Document::new("far");
|
||||
shaft(&mut doc, "Shaft", Vec3::ZERO, 3.0, 6.0);
|
||||
for i in 0..5 {
|
||||
block(&mut doc, &format!("Far{i}"), Vec3::new(200.0 * (i + 1) as f64, 0.0, 0.0), [50.0, 40.0, 30.0]);
|
||||
}
|
||||
let (work, threads) = marking_work(&mut doc, marking(Vec3::new(3.0, 0.0, 3.0)));
|
||||
assert_eq!(work.cylinder_search, 1, "only the shaft is searched");
|
||||
assert_eq!(threads.len(), 1);
|
||||
assert_eq!(threads[0].body, "Shaft");
|
||||
assert_eq!(threads[0].designation, "M6");
|
||||
assert!((threads[0].radius - 3.0).abs() < 1e-6 && !threads[0].internal);
|
||||
|
||||
// A tapped hole in a plate among far bodies: only the plate is searched for its wall.
|
||||
let mut doc = Document::new("tapped");
|
||||
block(&mut doc, "Plate", Vec3::ZERO, [60.0, 60.0, 12.0]);
|
||||
for i in 0..5 {
|
||||
block(&mut doc, &format!("Far{i}"), Vec3::new(200.0 * (i + 1) as f64, 0.0, 0.0), [50.0, 40.0, 30.0]);
|
||||
}
|
||||
let hole = FeatureKind::Hole {
|
||||
position: FacePick(Vec3::new(30.0, 30.0, 12.0)),
|
||||
at: None,
|
||||
direction: Dir(-Vec3::Z),
|
||||
diameter: "5".into(),
|
||||
depth: Some("10".into()),
|
||||
hole: HoleKind::default(),
|
||||
points: None,
|
||||
thread: Some("M6".into()),
|
||||
thread_depth: None,
|
||||
hand: crate::thread_forms::ThreadHand::Right,
|
||||
modeled: false,
|
||||
ease_out: None,
|
||||
to: None,
|
||||
references: Vec::new(),
|
||||
};
|
||||
let (work, threads) = marking_work(&mut doc, hole);
|
||||
assert_eq!(work.cylinder_search, 1, "only the plate is searched");
|
||||
assert_eq!(work.tapped_wall, 1, "only the plate's faces are read for the wall");
|
||||
assert_eq!(threads.len(), 1);
|
||||
assert_eq!(threads[0].body, "Plate");
|
||||
assert!(threads[0].internal && (threads[0].radius - 2.5).abs() < 1e-6);
|
||||
}
|
||||
|
||||
/// Bodies within reach of a tap marking are still searched: the shaft it lies on, a block 3 mm
|
||||
/// off (within a twentieth of the block's size, where the cylinder search itself looks), and one
|
||||
/// 3.6 mm off (just past it, but within the margin the boxes keep); a block 4.5 mm off is not.
|
||||
/// The marking stays on the shaft.
|
||||
#[test]
|
||||
fn a_tap_marking_still_searches_the_bodies_within_its_reach() {
|
||||
let mut doc = Document::new("near");
|
||||
shaft(&mut doc, "Shaft", Vec3::ZERO, 3.0, 6.0);
|
||||
// Each block is 50 x 40 x 30 (a twentieth of its size is 3.54 mm), its face x = 3 + gap.
|
||||
for (name, gap) in [("Close", 3.0), ("Edge", 3.6), ("Clear", 4.5)] {
|
||||
block(&mut doc, name, Vec3::new(3.0 + gap, -20.0, 0.0), [50.0, 40.0, 30.0]);
|
||||
}
|
||||
let (work, threads) = marking_work(&mut doc, marking(Vec3::new(3.0, 0.0, 3.0)));
|
||||
assert_eq!(work.cylinder_search, 3, "the shaft, Close and Edge are searched");
|
||||
assert_eq!(threads.len(), 1);
|
||||
assert_eq!(threads[0].body, "Shaft");
|
||||
|
||||
// A pin in a hole a hair wider: a marking on the pin, and one on the hole's wall.
|
||||
for (p, body, internal) in [(Vec3::new(2.0, 0.0, 5.0), "Pin", false), (Vec3::new(2.5, 0.0, 5.0), "Plate", true)] {
|
||||
let mut doc = Document::new("pin");
|
||||
block(&mut doc, "Plate", Vec3::new(-20.0, -20.0, 0.0), [40.0, 40.0, 10.0]);
|
||||
let bore = FeatureKind::Cylinder {
|
||||
base: point_expr(Vec3::new(0.0, 0.0, -1.0)),
|
||||
axis: Dir(Vec3::Z),
|
||||
radius: "2.5".into(),
|
||||
height: "12".into(),
|
||||
operation: Operation::Cut,
|
||||
references: Vec::new(),
|
||||
};
|
||||
doc.add_feature(bore, None).unwrap();
|
||||
shaft(&mut doc, "Pin", Vec3::ZERO, 2.0, 10.0);
|
||||
let mut kind = marking(p);
|
||||
if let FeatureKind::Thread { designation, .. } = &mut kind {
|
||||
*designation = None;
|
||||
}
|
||||
let (work, threads) = marking_work(&mut doc, kind);
|
||||
assert_eq!(work.cylinder_search, 2, "both bodies are searched for {body}");
|
||||
assert_eq!(threads.len(), 1);
|
||||
assert_eq!(threads[0].body, body);
|
||||
assert_eq!(threads[0].internal, internal);
|
||||
}
|
||||
}
|
||||
|
||||
/// A flat sheet of the default rule (2.5 mm steel) on a `w` x `h` rectangle from `corner` on XY.
|
||||
fn flat_sheet(doc: &mut Document, corner: Vec2, w: f64, h: f64) {
|
||||
let mut sk = Sketch::new();
|
||||
let p = [corner, corner + Vec2::new(w, 0.0), corner + Vec2::new(w, h), corner + Vec2::new(0.0, h)].map(|q| sk.add_point(q, None).unwrap());
|
||||
for k in 0..4 {
|
||||
sk.add_line_pts(p[k], p[(k + 1) % 4], None).unwrap();
|
||||
}
|
||||
let sketch = doc.add_feature(FeatureKind::Sketch { plane: PlaneRef::Origin { name: "XY".into() }, sketch: sk }, None).unwrap();
|
||||
doc.add_feature(FeatureKind::SheetBase { sketch: SketchId(sketch), profiles: ProfileSel::All, rule: None, flip: false }, None).unwrap();
|
||||
}
|
||||
|
||||
/// A hole of `diameter` at `position`, drilling along `direction` through all.
|
||||
fn hole(position: Vec3, direction: Vec3, diameter: f64) -> FeatureKind {
|
||||
serde_json::from_value(serde_json::json!({
|
||||
"type": "hole", "position": [position.x, position.y, position.z], "direction": [direction.x, direction.y, direction.z],
|
||||
"diameter": diameter.to_string(),
|
||||
}))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// The sheet metal bodies of a model.
|
||||
fn sheet_bodies(model: &Model) -> Vec<String> {
|
||||
model.state().sheets.iter().map(|s| s.body.clone()).collect()
|
||||
}
|
||||
|
||||
/// A cut leaves the sheets it cannot reach as they were, without checking that it would leave
|
||||
/// each one unchanged (#10226).
|
||||
#[test]
|
||||
fn a_cut_leaves_sheets_it_cannot_reach_as_they_were() {
|
||||
let mut doc = Document::new("far sheets");
|
||||
for i in 0..5 {
|
||||
flat_sheet(&mut doc, Vec2::new(200.0 * i as f64, 0.0), 150.0, 100.0);
|
||||
}
|
||||
block(&mut doc, "Plate", Vec3::new(0.0, -200.0, 0.0), [60.0, 60.0, 12.0]);
|
||||
let work = cut_work(&mut doc, cutter(Vec3::new(30.0, -170.0, -1.0), 3.0, 20.0));
|
||||
assert_eq!(work.cut, 1, "only the plate goes through a boolean");
|
||||
assert_eq!(work.kept, sheet_bodies(&work.model), "every sheet is left as it was");
|
||||
assert!(volume(&work.model, "Plate") < 43_200.0 - 300.0, "and the plate is cut");
|
||||
}
|
||||
|
||||
/// A tapped hole reads for its wall the faces of the bodies its drill line comes near: the plate
|
||||
/// it is drilled in, a block far below on the same line, and one whose side lies a radius off the
|
||||
/// line; not a block beside it. The marking stays in the plate's hole.
|
||||
#[test]
|
||||
fn a_tapped_hole_reads_the_bodies_along_its_drill_line() {
|
||||
let mut doc = Document::new("line");
|
||||
block(&mut doc, "Plate", Vec3::ZERO, [60.0, 60.0, 12.0]);
|
||||
block(&mut doc, "Below", Vec3::new(20.0, 20.0, -150.0), [20.0, 20.0, 20.0]);
|
||||
block(&mut doc, "Grazing", Vec3::new(32.5, 20.0, -60.0), [20.0, 20.0, 20.0]);
|
||||
block(&mut doc, "Beside", Vec3::new(50.0, 20.0, -60.0), [20.0, 20.0, 20.0]);
|
||||
let hole = FeatureKind::Hole {
|
||||
position: FacePick(Vec3::new(30.0, 30.0, 12.0)),
|
||||
at: None,
|
||||
direction: Dir(-Vec3::Z),
|
||||
diameter: "5".into(),
|
||||
depth: Some("10".into()),
|
||||
hole: HoleKind::default(),
|
||||
points: None,
|
||||
thread: Some("M6".into()),
|
||||
thread_depth: None,
|
||||
hand: crate::thread_forms::ThreadHand::Right,
|
||||
modeled: false,
|
||||
ease_out: None,
|
||||
to: None,
|
||||
references: Vec::new(),
|
||||
};
|
||||
let (work, threads) = marking_work(&mut doc, hole);
|
||||
assert_eq!(work.tapped_wall, 3, "Plate, Below and Grazing are read");
|
||||
assert_eq!(threads.len(), 1);
|
||||
assert_eq!(threads[0].body, "Plate");
|
||||
assert!(threads[0].internal && (threads[0].radius - 2.5).abs() < 1e-6);
|
||||
}
|
||||
|
||||
/// A hole leaves the sheets it cannot reach as they were, without drilling, checking or cutting
|
||||
/// each one: in a design of many sheet parts that was most of a hole's time (#10226).
|
||||
#[test]
|
||||
fn a_hole_leaves_sheets_it_cannot_reach_as_they_were() {
|
||||
let mut doc = Document::new("far sheets");
|
||||
for i in 0..5 {
|
||||
flat_sheet(&mut doc, Vec2::new(200.0 * i as f64, 0.0), 150.0, 100.0);
|
||||
}
|
||||
block(&mut doc, "Plate", Vec3::new(0.0, -200.0, 0.0), [60.0, 60.0, 12.0]);
|
||||
let work = cut_work(&mut doc, hole(Vec3::new(30.0, -170.0, 12.0), Vec3::new(0.0, 0.0, -1.0), 6.0));
|
||||
let sheets = sheet_bodies(&work.model);
|
||||
assert_eq!(sheets.len(), 5);
|
||||
assert_eq!(work.cut, 1, "only the plate goes through a boolean");
|
||||
assert_eq!(work.kept, sheets, "every sheet is left as it was");
|
||||
assert!(volume(&work.model, "Plate") < 43_200.0 - 300.0, "and the plate is drilled");
|
||||
}
|
||||
|
||||
/// A hole drilled into a sheet's rim still bores that sheet, beside sheets it cannot reach.
|
||||
#[test]
|
||||
fn a_hole_into_a_rim_still_bores_its_sheet_beside_far_ones() {
|
||||
let mut doc = Document::new("rim");
|
||||
for i in 0..4 {
|
||||
flat_sheet(&mut doc, Vec2::new(200.0 * i as f64, 0.0), 60.0, 40.0);
|
||||
}
|
||||
let mut model = Model::new();
|
||||
model.evaluate(&doc);
|
||||
let near = sheet_bodies(&model)[0].clone();
|
||||
let whole = volume(&model, &near);
|
||||
let d = 1.5;
|
||||
let work = cut_work(&mut doc, hole(Vec3::new(60.0, 20.0, 1.25), Vec3::new(-1.0, 0.0, 0.0), d));
|
||||
assert_eq!(work.cut, 1, "only the sheet the drill meets is bored");
|
||||
assert_eq!(work.kept, sheet_bodies(&work.model)[1..]);
|
||||
let state = work.model.state();
|
||||
let sheet = state.sheets.iter().find(|s| s.body == near).unwrap();
|
||||
assert_eq!(sheet.bores.len(), 1, "kept as a bore of the sheet");
|
||||
let removed = whole - volume(&work.model, &near);
|
||||
let bore = std::f64::consts::PI * (d / 2.0).powi(2) * 60.0;
|
||||
assert!((removed - bore).abs() < 0.02 * bore, "removed {removed} of {bore}");
|
||||
}
|
||||
|
||||
+336
-166
@@ -44,17 +44,8 @@ pub(super) fn eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature,
|
||||
{
|
||||
let mut scoped = ModelState::default();
|
||||
scoped.bodies.push(mb.clone());
|
||||
let (picked, split) = crate::naming::point_on_face(&scoped, name, at)
|
||||
.ok_or_else(|| DocError::Invalid("selected cylinder face no longer exists".into()))?;
|
||||
if split {
|
||||
return Err(DocError::Invalid("selected cylinder face was split".into()));
|
||||
}
|
||||
let (index, _, _) = crate::project::nearest_face(&mb.mesh(), picked)
|
||||
.ok_or_else(|| DocError::Invalid("selected cylinder face cannot be resolved".into()))?;
|
||||
if !crate::naming::face_has_name(&mb, index as usize, name) {
|
||||
return Err(DocError::Invalid("selected cylinder face is ambiguous".into()));
|
||||
}
|
||||
at = kernel::cylinder_emboss_anchor(&mb.body, index as usize, picked)?;
|
||||
let hit = crate::name_lookup::locate_face(&scoped, name, at).ok_or_else(|| DocError::reselect(name.as_str()))?;
|
||||
at = kernel::cylinder_emboss_anchor(&mb.body, hit.face, hit.point)?;
|
||||
}
|
||||
let result = kernel::emboss_cylinder_at(&mb.body, at, &plane, ®ions, if *deboss { -d } else { d })?;
|
||||
if let Some(slot) = st.bodies.get_mut(i) {
|
||||
@@ -220,7 +211,7 @@ pub(super) fn rib_tools(vals: &BTreeMap<String, Value>, st: &ModelState, f: &Fea
|
||||
fn planar_normal_at(b: &Body, p: Vec3) -> Option<Vec3> {
|
||||
let tol = (b.size() * 2e-3).max(1e-3);
|
||||
let faces = b.faces(tol).ok()?;
|
||||
let mesh = b.tessellate(tol).ok()?;
|
||||
let mesh = b.mesh(&solidcraft_kernel::MeshTolerance::chord(tol)).ok()?;
|
||||
let mut near = vec![f64::INFINITY; faces.len()];
|
||||
for (t, f) in mesh.triangles.iter().zip(&mesh.tri_face) {
|
||||
if let (Some([u, v, w]), Some(d)) = (mesh.tri(t), near.get_mut(*f as usize)) {
|
||||
@@ -601,10 +592,7 @@ pub(super) fn sheet_eval(
|
||||
}
|
||||
let rr = match radius {
|
||||
Some(e) => val(vals, e, Kind::Length)?,
|
||||
None => {
|
||||
let rule = st.sheets.get(i).map(|s| s.rule.clone()).unwrap_or_default();
|
||||
doc.rule_values(vals, &doc.sheet_rule(Some(&rule)))?.bend_radius
|
||||
}
|
||||
None => sheet_rule_values(doc, vals, st, i)?.bend_radius,
|
||||
};
|
||||
// The corner relief these flanges ask for in place of the rule's.
|
||||
let relief = sheet::ReliefOverride {
|
||||
@@ -684,7 +672,7 @@ pub(super) fn sheet_eval(
|
||||
tag_flanges(sh, n0, f.id);
|
||||
rebuild_sheet(st, i)
|
||||
}
|
||||
FeatureKind::SheetPerforate { diameter, pitch, margin, pattern, body } => {
|
||||
FeatureKind::SheetPerforate { diameter, pitch, margin, pattern, body, sketch, profiles } => {
|
||||
// Without a body name the field goes on the newest sheet, as the command's own body lookup does.
|
||||
let i = match body {
|
||||
Some(_) => sheet_for(st, body, None)?,
|
||||
@@ -706,8 +694,18 @@ pub(super) fn sheet_eval(
|
||||
"perforation needs diameter > 0, pitch > diameter and margin >= 0 (diameter {d}, pitch {pitch}, margin {margin})"
|
||||
)));
|
||||
}
|
||||
// The outline sketch's profiles, where they lie on the sheet now.
|
||||
let outline = match sketch {
|
||||
Some(sk) => {
|
||||
let ss = st.sketch(sk.0).ok_or_else(|| DocError::Unknown(format!("sketch {sk}")))?;
|
||||
let regions = select_profiles(ss, profiles.as_ref().unwrap_or(&ProfileSel::All))?;
|
||||
let sh = st.sheets.get(i).ok_or_else(|| DocError::Invalid("sheet".into()))?;
|
||||
Some(sh.perforation_outline(&ss.plane, ®ions)?)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
let sh = st.sheets.get_mut(i).ok_or_else(|| DocError::Invalid("sheet".into()))?;
|
||||
sh.perforations.push(crate::sheet::SheetPerforation { feature: f.id, diameter: d, pitch, margin, square, from_right: false });
|
||||
sh.perforations.push(crate::sheet::SheetPerforation { feature: f.id, diameter: d, pitch, margin, square, from_right: false, outline });
|
||||
Ok(())
|
||||
}
|
||||
FeatureKind::SheetHem { edges, length, gap, flip, body } => {
|
||||
@@ -715,10 +713,7 @@ pub(super) fn sheet_eval(
|
||||
let l = val(vals, length, Kind::Length)?;
|
||||
let g = match gap {
|
||||
Some(e) => val(vals, e, Kind::Length)?,
|
||||
None => {
|
||||
let rule = st.sheets.get(i).map(|s| s.rule.clone()).unwrap_or_default();
|
||||
doc.rule_values(vals, &doc.sheet_rule(Some(&rule)))?.hem_gap
|
||||
}
|
||||
None => sheet_rule_values(doc, vals, st, i)?.hem_gap,
|
||||
};
|
||||
let sh = st.sheets.get_mut(i).ok_or_else(|| DocError::Invalid("sheet".into()))?;
|
||||
let picks = sheet_picks(sh, f, &crate::plain(edges), warning);
|
||||
@@ -746,10 +741,7 @@ pub(super) fn sheet_eval(
|
||||
let ang = val(vals, angle, Kind::Angle)?;
|
||||
let rr = match radius {
|
||||
Some(e) => val(vals, e, Kind::Length)?,
|
||||
None => {
|
||||
let rule = st.sheets.get(i).map(|s| s.rule.clone()).unwrap_or_default();
|
||||
doc.rule_values(vals, &doc.sheet_rule(Some(&rule)))?.bend_radius
|
||||
}
|
||||
None => sheet_rule_values(doc, vals, st, i)?.bend_radius,
|
||||
};
|
||||
// A fixed point that touches no material at all (e.g. far above the sheet) still picks
|
||||
// the side it lies on, so a stored fold keeps recomputing; it says so (#371).
|
||||
@@ -789,31 +781,26 @@ pub(super) fn sheet_eval(
|
||||
FeatureKind::SheetConvert { body, face, rule, bends } if !bends.is_empty() => {
|
||||
let mb = st.body(body).cloned().ok_or_else(|| DocError::Unknown(format!("body `{body}`")))?;
|
||||
let r = doc.sheet_rule(rule.as_deref());
|
||||
let rv = doc.rule_values(vals, &r)?;
|
||||
// The bend edges by name first; a name that is gone keeps its point, with a warning.
|
||||
let values = |t: Option<f64>| doc.rule_values_at(vals, &r, t);
|
||||
// The bend edges by name where they have one: a name that no longer finds its edge
|
||||
// fails the conversion, asking for the edge to be picked again.
|
||||
let mut picks = Vec::with_capacity(bends.len());
|
||||
for (i, p) in bends.iter().enumerate() {
|
||||
let name = f.edge_names.get(i).filter(|n| !n.is_empty());
|
||||
picks.push(match name.map(|n| (n, crate::naming::locate_edge(&mb, n, p.0))) {
|
||||
Some((n, Ok(hit))) => {
|
||||
picks.push(match name {
|
||||
Some(n) => {
|
||||
let hit = crate::naming::locate_edge(&mb, n, p.0).map_err(|_| DocError::reselect(n.as_str()))?;
|
||||
if let Some(note) = hit.note(n) {
|
||||
append_warning(warning, note);
|
||||
}
|
||||
hit.point
|
||||
}
|
||||
Some((n, Err(_))) => {
|
||||
append_warning(
|
||||
warning,
|
||||
format!("edge `{}` no longer exists; using the edge at the picked point", crate::name_lookup::quoted(n)),
|
||||
);
|
||||
p.0
|
||||
}
|
||||
None => p.0,
|
||||
});
|
||||
}
|
||||
let placement = doc.component_transform(doc.model_body_component(&mb));
|
||||
let body_to_world = doc.model_body_offset(&mb).map(|offset| crate::mat_mul(&placement, offset)).unwrap_or(placement);
|
||||
let (mut sh, notes) = sheet::convert_body(&mb, &r.name, &rv, face.0, &picks, &body_to_world)?;
|
||||
let (mut sh, notes) = sheet::convert_body(&mb, &r.name, &values, face.0, &picks, &body_to_world)?;
|
||||
for note in notes {
|
||||
append_warning(warning, note);
|
||||
}
|
||||
@@ -832,11 +819,11 @@ pub(super) fn sheet_eval(
|
||||
// thin plate that thousandth passes the thickness: the far side's triangles counted as
|
||||
// this face's (no outline was left), and a hole of a smaller radius than that fell out.
|
||||
let mut tol = (mb.body.size() * 1e-3).max(1e-3);
|
||||
let mut mesh = mb.body.tessellate(tol)?;
|
||||
let mut mesh = mb.body.mesh(&solidcraft_kernel::MeshTolerance::chord(tol))?;
|
||||
let (mut lo, mut hi) = extent(&mesh);
|
||||
if hi - lo > 0.0 && tol > (hi - lo) * 0.1 {
|
||||
tol = ((hi - lo) * 0.1).max(1e-6);
|
||||
mesh = mb.body.tessellate(tol)?;
|
||||
mesh = mb.body.mesh(&solidcraft_kernel::MeshTolerance::chord(tol))?;
|
||||
(lo, hi) = extent(&mesh);
|
||||
}
|
||||
let t = hi - lo;
|
||||
@@ -864,8 +851,7 @@ pub(super) fn sheet_eval(
|
||||
));
|
||||
}
|
||||
let r = doc.sheet_rule(rule.as_deref());
|
||||
let mut rv = doc.rule_values(vals, &r)?;
|
||||
rv.thickness = t;
|
||||
let rv = doc.rule_values_at(vals, &r, Some(t))?;
|
||||
let mut sh = sheet::base_flange(body, &r.name, &rv, plane, Region2 { outer: region.outer.clone(), holes: Vec::new() });
|
||||
sh.holes = region.holes.iter().map(|h| Region2 { outer: h.ccw(), holes: Vec::new() }).collect();
|
||||
let mut work = sheet::FlatWork::new();
|
||||
@@ -878,9 +864,21 @@ pub(super) fn sheet_eval(
|
||||
}
|
||||
}
|
||||
|
||||
/// Current unmodified extrusion caps, including placement, certify the tool's depth.
|
||||
/// Tessellation can drift in thickness at distant coordinates and cannot certify a slab.
|
||||
/// The tool's depth along the sketch normal (from `source`), the roundoff of that depth and the
|
||||
/// tool's current footprint where it differs from the profile: from its current unmodified
|
||||
/// extrusion caps, including placement, else from its exact record proving it a uniform prism
|
||||
/// along the normal (a To Object tool trimmed by the body it runs to, whose boolean leaves no
|
||||
/// extrusion faces: its section is its footprint). Tessellation can drift in thickness at
|
||||
/// distant coordinates and cannot certify a slab.
|
||||
fn sheet_tool_span(body: &Body, source: &Plane) -> Option<(f64, f64, f64, Option<(Plane, Region2)>)> {
|
||||
extrusion_tool_span(body, source).or_else(|| {
|
||||
let prism = kernel::certified_prism_section(body, source).ok().flatten()?;
|
||||
(prism.tolerance <= 1e-7).then_some((prism.lo, prism.hi, prism.tolerance, Some((*source, prism.region))))
|
||||
})
|
||||
}
|
||||
|
||||
/// [`sheet_tool_span`] from the tool's extrusion caps.
|
||||
fn extrusion_tool_span(body: &Body, source: &Plane) -> Option<(f64, f64, f64, Option<(Plane, Region2)>)> {
|
||||
use kernel::FaceSource;
|
||||
#[derive(PartialEq)]
|
||||
enum Signature<'a> {
|
||||
@@ -1024,6 +1022,164 @@ fn extrude_cut(st: &ModelState, f: &Feature) -> Result<Option<ExtrudeCut>> {
|
||||
Ok(Some(ExtrudeCut { plane: ss.plane, regions, text, operation: *operation, bound: extent.to.is_some() }))
|
||||
}
|
||||
|
||||
/// An untapered extrude join one sheet thick in the plane of a panel of sheet metal (the base or
|
||||
/// a flange's panel, unfolded or folded) that touches that panel and nothing else of the sheet:
|
||||
/// the material becomes part of the panel's outline, so the flat pattern, its DXF and a refold
|
||||
/// keep it, and it follows its sketch's parameters as a cut made there does. Cut-outs it covers
|
||||
/// are filled. Returns the sheets it took; none (and the generic path's warning) for any other
|
||||
/// join: another depth, out of every panel's plane, reaching into a bend zone or another panel,
|
||||
/// apart from the sheet, or over a pocket, label, bore or perforation.
|
||||
pub(super) fn sheet_join(
|
||||
f: &Feature,
|
||||
st: &mut ModelState,
|
||||
tools: &[Body],
|
||||
targets: &[String],
|
||||
constructor: Option<&ExtrudeConstructor>,
|
||||
) -> Result<Vec<String>> {
|
||||
let FeatureKind::Extrude { sketch, profiles, operation: Operation::Join, extent, .. } = &f.kind else { return Ok(Vec::new()) };
|
||||
if extent.taper.is_some() || tools.is_empty() || !any_eligible_sheet(f, st, targets) {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
let Some(ss) = st.sketch(sketch.0).cloned() else { return Ok(Vec::new()) };
|
||||
// The depth the extrude was given, along its sketch's normal: only an untrimmed extrude has one.
|
||||
let Some(c) = constructor.filter(|c| c.plane == ss.plane) else { return Ok(Vec::new()) };
|
||||
let regions = solidcraft_sketch::merge_regions(&select_profiles(&ss, profiles)?);
|
||||
let plane = ss.plane;
|
||||
let n = plane.normal();
|
||||
let (lo, hi) = (c.interval.0.min(c.interval.1), c.interval.0.max(c.interval.1));
|
||||
let roundoff = 128.0 * f64::EPSILON * (plane.origin.len() + lo.abs() + hi.abs()).max(1.0);
|
||||
let mut work = sheet::FlatWork::new();
|
||||
let mut plans = Vec::new();
|
||||
for original in &st.sheets {
|
||||
if !sheet_eligible(f, st, targets, original) {
|
||||
continue;
|
||||
}
|
||||
let Some(sh) = sheet_join_one(original, &plane, n, ®ions, (lo, hi, roundoff), &mut work)? else { return Ok(Vec::new()) };
|
||||
let body = sh.solid()?;
|
||||
plans.push((sh, body));
|
||||
}
|
||||
let handled = plans.iter().map(|(sh, _)| sh.body.clone()).collect();
|
||||
for (sh, body) in plans {
|
||||
if let Some(mb) = st.bodies.iter_mut().find(|b| b.name == sh.body) {
|
||||
replace_sheet_body(mb, body);
|
||||
}
|
||||
if let Some(target) = st.sheets.iter_mut().find(|s| s.body == sh.body) {
|
||||
*target = sh;
|
||||
}
|
||||
}
|
||||
Ok(handled)
|
||||
}
|
||||
|
||||
/// [`sheet_join`] on one sheet: the sheet with each of the join's `regions` (in `plane`, the
|
||||
/// depth `span` along its normal `n`) taken into the one panel it fits, or None where one of them
|
||||
/// fits none.
|
||||
fn sheet_join_one(
|
||||
original: &sheet::SheetBody,
|
||||
plane: &Plane,
|
||||
n: Vec3,
|
||||
regions: &[Region2],
|
||||
span: (f64, f64, f64),
|
||||
work: &mut sheet::FlatWork,
|
||||
) -> Result<Option<sheet::SheetBody>> {
|
||||
let (lo, hi, roundoff) = span;
|
||||
let sh = original;
|
||||
let region_of = |pi: Option<usize>| match pi {
|
||||
None => Some(sh.base.clone()),
|
||||
Some(i) => sh.flanges.get(i).map(|f| sh.panel(f)),
|
||||
};
|
||||
let all: Vec<Option<usize>> = std::iter::once(None).chain((0..sh.flanges.len()).map(Some)).collect();
|
||||
// The panels whose faces are the sheet's thickness apart along the sketch's normal, the join
|
||||
// filling exactly that slab.
|
||||
let pieces: Vec<Option<usize>> = all
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|&pi| {
|
||||
let w = sh.world(pi);
|
||||
let sign = crate::apply_vector(&w, Vec3::Z).dot(n);
|
||||
if sign.abs() < 1.0 - 1e-9 {
|
||||
return false;
|
||||
}
|
||||
let zorigin = (apply_point(&w, Vec3::ZERO) - plane.origin).dot(n);
|
||||
let (a, b) = ((lo - zorigin) / sign, (hi - zorigin) / sign);
|
||||
let tol = 1e-6 + roundoff;
|
||||
a.min(b).abs() <= tol && (a.max(b) - sh.t).abs() <= tol
|
||||
})
|
||||
.collect();
|
||||
let zones: Vec<Region2> = sh.flanges.iter().map(|f| sh.zone(f)).collect();
|
||||
let touches = |a: &Region2, b: &Region2, work: &mut sheet::FlatWork| -> Result<bool> {
|
||||
Ok(sheet::material_boolean(std::slice::from_ref(a), std::slice::from_ref(b), kernel::BoolOp::Union, work)?.len() == 1)
|
||||
};
|
||||
// Each region joins the one panel it is one piece with, apart from every bend zone and the
|
||||
// other panels (touching one along an edge, the flat pattern would lose the cut between them).
|
||||
let mut fits: Vec<(Option<usize>, Region2)> = Vec::new();
|
||||
for r in regions {
|
||||
let mut found = None;
|
||||
for &pi in &pieces {
|
||||
let Some(piece) = region_of(pi) else { continue };
|
||||
let flat = sh.region_to_flat(pi, plane, r)?;
|
||||
if !touches(&piece, &flat, work)? {
|
||||
continue;
|
||||
}
|
||||
let mut apart = true;
|
||||
for other in zones.iter().cloned().chain(all.iter().filter(|&&q| q != pi).filter_map(|&q| region_of(q))) {
|
||||
if touches(&other, &flat, work)? {
|
||||
apart = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !apart {
|
||||
continue;
|
||||
}
|
||||
if found.is_some() {
|
||||
// Two panels in the plane both take it.
|
||||
return Ok(None);
|
||||
}
|
||||
found = Some((pi, flat));
|
||||
}
|
||||
let Some(fit) = found else { return Ok(None) };
|
||||
fits.push(fit);
|
||||
}
|
||||
// Pockets, labels, bores and perforations keep their own outlines: not under a join yet.
|
||||
if !sh.bores.is_empty() || !sh.perforations.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
let flats: Vec<Region2> = fits.iter().map(|(_, r)| r.clone()).collect();
|
||||
let covered: f64 = {
|
||||
let kept: Vec<Region2> = sh.pockets.iter().map(|p| p.region.clone()).chain(sh.labels.iter().cloned()).collect();
|
||||
sheet::material_boolean(&kept, &flats, kernel::BoolOp::Intersect, work)?.iter().map(Region2::area).sum()
|
||||
};
|
||||
if covered > 1e-9 {
|
||||
return Ok(None);
|
||||
}
|
||||
let mut sh = original.clone();
|
||||
for &pi in &all {
|
||||
let mine: Vec<Region2> = fits.iter().filter(|(q, _)| *q == pi).map(|(_, r)| r.clone()).collect();
|
||||
if mine.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let Some(piece) = region_of(pi) else { return Ok(None) };
|
||||
let joined = sheet::material_boolean(std::slice::from_ref(&piece), &mine, kernel::BoolOp::Union, work)?;
|
||||
let [one] = joined.as_slice() else { return Ok(None) };
|
||||
match pi {
|
||||
None => sh.base = one.clone(),
|
||||
Some(i) => {
|
||||
if !one.holes.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
let Some(f) = sh.flanges.get_mut(i) else { return Ok(None) };
|
||||
f.exact = Some(one.outer.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Cut-outs it covers are filled.
|
||||
let mut holes = Vec::with_capacity(sh.holes.len());
|
||||
for h in &sh.holes {
|
||||
holes.extend(sheet::material_boolean(std::slice::from_ref(h), &flats, kernel::BoolOp::Cut, work)?);
|
||||
}
|
||||
sh.holes = holes;
|
||||
Ok(Some(sh))
|
||||
}
|
||||
|
||||
/// Whether a cut of `f` with these targets (else its participants, else its component's
|
||||
/// bodies) machines the sheet `original`.
|
||||
fn sheet_eligible(f: &Feature, st: &ModelState, targets: &[String], original: &crate::sheet::SheetBody) -> bool {
|
||||
@@ -1059,15 +1215,17 @@ pub(super) fn sheet_primitive_cut(
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
// The first sheet the primitive meets decides the machining direction. Sheets clear of it
|
||||
// by their boxes are passed over; the one sheet left needs no boolean to show it is met (that
|
||||
// boolean came out empty against a sheet carrying a label's cut-outs, and the hole was then
|
||||
// cut into the solid without a place in the flat pattern).
|
||||
let tool_box = body_box(tool);
|
||||
let near: Vec<&sheet::SheetBody> = st
|
||||
.sheets
|
||||
.iter()
|
||||
.filter(|sh| sheet_eligible(f, st, &[], sh) && st.body(&sh.body).is_some_and(|mb| !boxes_apart(&mesh_box(mb), &tool_box)))
|
||||
.collect();
|
||||
// by their exact boxes ([`kernel::apart`]) are passed over; the one sheet left needs no
|
||||
// boolean to show it is met (that boolean came out empty against a sheet carrying a label's
|
||||
// cut-outs, and the hole was then cut into the solid without a place in the flat pattern).
|
||||
let mut near: Vec<&sheet::SheetBody> = Vec::new();
|
||||
for sh in st.sheets.iter().filter(|sh| sheet_eligible(f, st, &[], sh)) {
|
||||
if let Some(mb) = st.body(&sh.body)
|
||||
&& !kernel::apart(&mb.body, tool)?
|
||||
{
|
||||
near.push(sh);
|
||||
}
|
||||
}
|
||||
let mut normal = None;
|
||||
for sh in &near {
|
||||
if let Some(mb) = st.body(&sh.body)
|
||||
@@ -1090,24 +1248,61 @@ pub(super) fn sheet_primitive_cut(
|
||||
sheet_prism_cut(f, st, &plane, std::slice::from_ref(®ion), operation, &tools, &[], None, false, &[], warning)
|
||||
}
|
||||
|
||||
/// How a hole's straight drill machines the sheets it meets, from the panels it passes through.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(super) enum Drill {
|
||||
/// Square across every panel it meets: a cut-out or pocket of the flat pattern
|
||||
/// ([`sheet_primitive_cut`]).
|
||||
Across,
|
||||
/// Along every panel it meets, into a rim: a bore of each ([`sheet_bore_cut`]).
|
||||
Along,
|
||||
}
|
||||
|
||||
/// Which [`Drill`] a hole's straight drill is for the sheets `f` machines; None when it meets
|
||||
/// none of them. A box is a prism across its sheet. A drill square to some panels it meets and
|
||||
/// along others, or at an angle to one, has no exact place in the flat pattern and is refused.
|
||||
pub(super) fn sheet_drill(f: &Feature, st: &ModelState, prism: PrimitivePrism) -> Result<Option<Drill>> {
|
||||
let PrimitivePrism::Cylinder { base, axis, radius, height } = prism else { return Ok(Some(Drill::Across)) };
|
||||
let mut squares = Vec::new();
|
||||
for sh in st.sheets.iter().filter(|sh| sheet_eligible(f, st, &[], sh)) {
|
||||
squares.extend(sh.drilled_panels(base, axis, radius, height)?.into_iter().map(|(_, square)| square));
|
||||
}
|
||||
if squares.is_empty() {
|
||||
Ok(None)
|
||||
} else if squares.iter().all(|s| *s >= 1.0 - 1e-6) {
|
||||
Ok(Some(Drill::Across))
|
||||
} else if squares.iter().all(|s| *s <= 1e-6) {
|
||||
Ok(Some(Drill::Along))
|
||||
} else {
|
||||
Err(DocError::Invalid("this hole crosses a sheet panel at an angle; drill it square to the panel, or along it into a rim".into()))
|
||||
}
|
||||
}
|
||||
|
||||
/// A hole's straight drill along a panel of a sheet (into its rim), kept as a bore of each
|
||||
/// panel it meets (see [`crate::sheet::SheetBore`]), so the sheet still unfolds. Returns the
|
||||
/// sheet bodies it machined; None, with the model as it was, when the drill is not a plain
|
||||
/// cylinder or a sheet it meets can't keep it that way (it runs into a bend, or crosses a
|
||||
/// panel at an angle).
|
||||
/// sheet bodies it machined; None when the drill is not a plain cylinder or meets no sheet.
|
||||
pub(super) fn sheet_bore_cut(f: &Feature, st: &mut ModelState, prism: PrimitivePrism) -> Result<Option<Vec<String>>> {
|
||||
let PrimitivePrism::Cylinder { base, axis, radius, height } = prism else { return Ok(None) };
|
||||
// Drilling a sheet builds each of its panels and runs a boolean on it, only to find that a
|
||||
// drill far off meets none (#10226): a sheet whose box lies clear of the drill's is left out.
|
||||
// Both boxes are the exact ones, grown by the bodies' tolerances, as for the other sheet
|
||||
// cuts. A drill a sheet refuses whatever it meets (not a cylinder, or the
|
||||
// sheet holds all the bores it may) is still put to every sheet.
|
||||
let reach = drill_box(base, axis, radius, height);
|
||||
let mut plans = Vec::new();
|
||||
for original in &st.sheets {
|
||||
if !sheet_eligible(f, st, &[], original) {
|
||||
continue;
|
||||
}
|
||||
if !original.bores_full() && st.body(&original.body).is_some_and(|mb| boxes_apart(&exact_box(&mb.body), &reach)) {
|
||||
continue;
|
||||
}
|
||||
let mut sh = original.clone();
|
||||
#[cfg(test)]
|
||||
super::cost_tests::count(|c| c.cut += 1);
|
||||
match sh.add_bore(base, axis, radius, height) {
|
||||
Ok(true) => {}
|
||||
Ok(false) => continue,
|
||||
// Refused here, the hole keeps the message of the machining it was first tried as.
|
||||
Err(DocError::Invalid(_)) => return Ok(None),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
let body = sh.solid()?;
|
||||
@@ -1129,6 +1324,16 @@ pub(super) fn sheet_bore_cut(f: &Feature, st: &mut ModelState, prism: PrimitiveP
|
||||
Ok(Some(handled))
|
||||
}
|
||||
|
||||
/// [`exact_box`] of a hole's drill as [`crate::sheet::SheetBody::add_bore`] makes it; None
|
||||
/// (never apart) for a drill it refuses.
|
||||
fn drill_box(base: Vec3, axis: Vec3, radius: f64, height: f64) -> Option<Aabb3> {
|
||||
let axis = axis.normalized()?;
|
||||
if ![base.x, base.y, base.z, radius, height].iter().all(|v| v.is_finite()) || !(radius > 0.0) || height == 0.0 {
|
||||
return None;
|
||||
}
|
||||
exact_box(&kernel::cylinder(base, axis, radius, height).ok()?)
|
||||
}
|
||||
|
||||
/// The most hole tools machined in one pass by [`sheet_drill_cuts`].
|
||||
const MAX_DRILLS_AT_ONCE: usize = 1000;
|
||||
|
||||
@@ -1137,8 +1342,8 @@ const MAX_DRILLS_AT_ONCE: usize = 1000;
|
||||
/// of them, rather than one per drill, each on a sheet machined by the ones before.
|
||||
/// The drills are the cut-outs and pockets [`sheet_primitive_cut`] makes of them one at a time.
|
||||
/// None when they can't all be taken this way (they are not all cylinders along one axis that
|
||||
/// every sheet they may machine takes square, or a sheet keeps one some other way): the caller
|
||||
/// then puts the model back and drills them one by one.
|
||||
/// every sheet they may machine takes square, or a depth of them meets no sheet): the caller then
|
||||
/// puts the model back and drills them one by one.
|
||||
pub(super) fn sheet_drill_cuts(
|
||||
f: &Feature,
|
||||
st: &mut ModelState,
|
||||
@@ -1352,9 +1557,6 @@ fn sheet_prism_cut(
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
// Boxes around the tools: a tool clear of a bend by its box needs no boolean to show it (a
|
||||
// label's letters against every bend and flange of a tray).
|
||||
let tool_boxes: Vec<Option<Aabb3>> = tools.iter().map(body_box).collect();
|
||||
let mut plans = Vec::new();
|
||||
let mut notes = Vec::new();
|
||||
'sheets: for original in &st.sheets {
|
||||
@@ -1374,12 +1576,10 @@ fn sheet_prism_cut(
|
||||
// is exact, and a cut that machines nothing there is checked against the solid after it.
|
||||
// (That boolean cost each label hundreds of milliseconds, and against a sheet already
|
||||
// carrying a label's cut-outs it came out empty: the next cut was dropped.)
|
||||
// A tool clear of a bend by its exact box (a label's letters against every bend and
|
||||
// flange of a tray) costs no intersection work: the boolean answers it from the boxes.
|
||||
for bends in sh.unmodeled_solids(plane)? {
|
||||
let bends_box = body_box(&bends);
|
||||
for (tool, tool_box) in tools.iter().zip(&tool_boxes) {
|
||||
if boxes_apart(&bends_box, tool_box) {
|
||||
continue;
|
||||
}
|
||||
for tool in tools {
|
||||
if kernel::boolean(&bends, tool, kernel::BoolOp::Intersect)?.is_some() {
|
||||
return Err(DocError::Invalid(
|
||||
"this machining intersects a folded bend or a nonparallel panel; unfold the sheet before machining that material".into(),
|
||||
@@ -1431,6 +1631,13 @@ fn sheet_prism_cut(
|
||||
}
|
||||
let (a, b) = nominal_depth.unwrap_or((a, b));
|
||||
let (z0, z1) = (a.min(b).max(0.0), a.max(b).min(sh.t));
|
||||
// A depth within a few units in the last place of the numbers it is worked out from
|
||||
// is on the face: a flange panel's frame, turned by the cosine and sine of its angle,
|
||||
// puts its faces an ulp (2e-16 mm) off the tool's cap they meet. A thinner layer is
|
||||
// the tool's own and stays (refused below when under the resolution).
|
||||
let ulps =
|
||||
4.0 * f64::EPSILON * (apply_point(&w, Vec3::ZERO).len() + plane.origin.len() + span.0.abs() + span.1.abs() + sh.t) / sign.abs();
|
||||
let (z0, z1) = (if z0 <= ulps { 0.0 } else { z0 }, if sh.t - z1 <= ulps { sh.t } else { z1 });
|
||||
// Sub-resolution layers must not be silently erased by endpoint snapping.
|
||||
let depth_resolved = ![z0, sh.t - z1, z1 - z0].into_iter().any(|d| d > 0.0 && d < 1e-6);
|
||||
labels.extend(letters.iter().filter_map(|r| sh.region_to_flat(pi, plane, r).ok()));
|
||||
@@ -1557,25 +1764,36 @@ fn sheet_prism_cut(
|
||||
Ok(handled)
|
||||
}
|
||||
|
||||
/// A box around `body`, grown past its mesh's deflection from the exact faces (and a hair
|
||||
/// more): bodies whose boxes are apart do not meet. None when it can't be worked out.
|
||||
fn body_box(body: &Body) -> Option<Aabb3> {
|
||||
let tol = (body.size() * 1e-3).max(1e-4);
|
||||
padded_box(&body.tessellate(tol).ok()?, tol)
|
||||
/// The exact box of `body`, grown by twice its tolerance: bodies whose boxes are apart do not
|
||||
/// meet. None when it can't be worked out.
|
||||
pub(super) fn exact_box(body: &Body) -> Option<Aabb3> {
|
||||
let b = body.exact_bounds().ok()?;
|
||||
(!b.is_empty() && b.min.is_finite() && b.max.is_finite()).then(|| b.inflated(2.0 * body.record().tolerance + 1e-6))
|
||||
}
|
||||
|
||||
/// [`body_box`] of a model body, from the mesh it keeps.
|
||||
fn mesh_box(mb: &ModelBody) -> Option<Aabb3> {
|
||||
padded_box(&mb.mesh(), mb.display_tol())
|
||||
}
|
||||
|
||||
fn padded_box(mesh: &solidcraft_geom::Mesh, tol: f64) -> Option<Aabb3> {
|
||||
let b = mesh.bounds();
|
||||
(!b.is_empty() && b.min.is_finite() && b.max.is_finite()).then(|| b.inflated(2.0 * tol + 1e-6))
|
||||
/// Whether the line through `at` along `dir` is known to pass farther than `reach` from the
|
||||
/// exact box of `mb` ([`exact_box`]) everywhere, and so from every point of the body. Unknown is
|
||||
/// never.
|
||||
pub(super) fn line_beyond_reach(mb: &ModelBody, at: Vec3, dir: Vec3, reach: f64) -> bool {
|
||||
let Some(b) = exact_box(&mb.body) else { return false };
|
||||
let b = b.inflated(reach);
|
||||
let (mut lo, mut hi) = (f64::NEG_INFINITY, f64::INFINITY);
|
||||
for (a, d, min, max) in [(at.x, dir.x, b.min.x, b.max.x), (at.y, dir.y, b.min.y, b.max.y), (at.z, dir.z, b.min.z, b.max.z)] {
|
||||
if d == 0.0 {
|
||||
if a < min || a > max {
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let (t0, t1) = ((min - a) / d, (max - a) / d);
|
||||
lo = lo.max(t0.min(t1));
|
||||
hi = hi.min(t0.max(t1));
|
||||
}
|
||||
lo > hi
|
||||
}
|
||||
|
||||
/// Whether two boxes are known to be apart (an unknown box is never apart).
|
||||
fn boxes_apart(a: &Option<Aabb3>, b: &Option<Aabb3>) -> bool {
|
||||
pub(super) fn boxes_apart(a: &Option<Aabb3>, b: &Option<Aabb3>) -> bool {
|
||||
let (Some(a), Some(b)) = (a, b) else { return false };
|
||||
a.max.x < b.min.x || b.max.x < a.min.x || a.max.y < b.min.y || b.max.y < a.min.y || a.max.z < b.min.z || b.max.z < a.min.z
|
||||
}
|
||||
@@ -1857,7 +2075,7 @@ impl FaceSlice<'_> {
|
||||
)));
|
||||
}
|
||||
// The floors' outlines, from a mesh fine enough to mark them by.
|
||||
let fine = body.tessellate((t * 1e-2).min(tol).max(1e-4))?;
|
||||
let fine = body.mesh(&solidcraft_kernel::MeshTolerance::chord((t * 1e-2).min(tol).max(1e-4)))?;
|
||||
floors.sort_by(|a, b| depth_of(a.1).total_cmp(&depth_of(b.1)));
|
||||
// Floors at one depth are one pocket where they meet.
|
||||
let mut groups: Vec<(f64, Vec<Region2>)> = Vec::new();
|
||||
@@ -1907,27 +2125,6 @@ fn local_frame(z: Vec3, x: Option<Vec3>) -> Result<(Vec3, Vec3, Vec3)> {
|
||||
Ok((x, y, z))
|
||||
}
|
||||
|
||||
/// Offset a closed polygon to its left by `d` (mitred corners).
|
||||
fn offset_left(p: &[Vec2], d: f64) -> Vec<Vec2> {
|
||||
let m = p.len();
|
||||
let line = |i: usize| -> Option<(Vec2, Vec2)> {
|
||||
let (a, b) = (*p.get(i)?, *p.get((i + 1) % m)?);
|
||||
let t = (b - a).normalized()?;
|
||||
Some((a + Vec2::new(-t.y, t.x) * d, t))
|
||||
};
|
||||
(0..m)
|
||||
.filter_map(|i| {
|
||||
let (p0, d0) = line((i + m - 1) % m)?;
|
||||
let (p1, d1) = line(i)?;
|
||||
let den = d0.cross(d1);
|
||||
if den.abs() < 1e-12 {
|
||||
return Some(p1);
|
||||
}
|
||||
Some(p0 + d0 * ((p1 - p0).cross(d1) / den))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// The smallest size a plastic feature's lengths may have, in millimetres. Thinner walls and
|
||||
/// shorter arms than this are below what the solid booleans can resolve.
|
||||
const PLASTIC_MIN_SIZE: f64 = 0.01;
|
||||
@@ -1949,7 +2146,7 @@ pub(super) fn plastic_eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &F
|
||||
let rule = |target: &str| -> Result<Option<crate::plastic::PlasticValues>> {
|
||||
doc.plastic_rule_for(target).map(|r| doc.plastic_values(vals, &r)).transpose()
|
||||
};
|
||||
// Tools start a little inside the material so they overlap it instead of touching a face.
|
||||
// Each tool stands on the face as drawn: SMLib joins and cuts touching faces exactly.
|
||||
match &f.kind {
|
||||
FeatureKind::Boss {
|
||||
position,
|
||||
@@ -1981,16 +2178,14 @@ pub(super) fn plastic_eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &F
|
||||
if r_top <= 0.0 {
|
||||
return Err(DocError::Invalid("the draft closes the boss before its top".into()));
|
||||
}
|
||||
let delta = (h * 0.05).clamp(1e-3, 0.5);
|
||||
let (x, _, z) = local_frame(direction.0, None)?;
|
||||
// The post: a (tapered) cylinder from a little inside the face.
|
||||
let base = Plane::new(position.0 - z * delta, x, z.cross(x)).ok_or_else(|| DocError::Invalid("boss frame".into()))?;
|
||||
let r_in = r0 + delta * dr.tan();
|
||||
let disc = Region2 { outer: Loop2::circle(Vec2::ZERO, r_in), holes: Vec::new() };
|
||||
// The post: a (tapered) cylinder standing on the face.
|
||||
let base = Plane::new(position.0, x, z.cross(x)).ok_or_else(|| DocError::Invalid("boss frame".into()))?;
|
||||
let disc = Region2 { outer: Loop2::circle(Vec2::ZERO, r0), holes: Vec::new() };
|
||||
let post = if dr.abs() > 1e-12 {
|
||||
vec![kernel::extrude_tapered(&base, &disc, h + delta, 1.0, -dr)?]
|
||||
vec![kernel::extrude_tapered(&base, &disc, h, 1.0, -dr, kernel::DraftCorner::default())?]
|
||||
} else {
|
||||
kernel::extrude(&base, &[disc], 0.0, h + delta)?
|
||||
kernel::extrude(&base, &[disc], 0.0, h)?
|
||||
};
|
||||
let mut tools: Vec<Traced> = post.into_iter().map(|b| crate::lineage::part_tool(b, "post")).collect();
|
||||
if let Some(n) = ribs {
|
||||
@@ -2029,7 +2224,7 @@ pub(super) fn plastic_eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &F
|
||||
]),
|
||||
holes: Vec::new(),
|
||||
};
|
||||
tools.extend(kernel::extrude(&rp, &[rect], -delta, hr)?.into_iter().map(|b| crate::lineage::part_tool(b, &format!("rib{k}"))));
|
||||
tools.extend(kernel::extrude(&rp, &[rect], 0.0, hr)?.into_iter().map(|b| crate::lineage::part_tool(b, &format!("rib{k}"))));
|
||||
}
|
||||
}
|
||||
apply_traced(doc, st, f, tools, Operation::Join, std::slice::from_ref(&target))?;
|
||||
@@ -2054,7 +2249,7 @@ pub(super) fn plastic_eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &F
|
||||
return Err(DocError::Invalid("the boss hole must fit inside the boss".into()));
|
||||
}
|
||||
let hp = Plane::new(position.0, x, z.cross(x)).ok_or_else(|| DocError::Invalid("hole frame".into()))?;
|
||||
let tool = kernel::extrude(&hp, &[Region2 { outer: Loop2::circle(Vec2::ZERO, rh), holes: Vec::new() }], h - depth, h + delta)?;
|
||||
let tool = kernel::extrude(&hp, &[Region2 { outer: Loop2::circle(Vec2::ZERO, rh), holes: Vec::new() }], h - depth, h)?;
|
||||
let tool = tool.into_iter().map(|b| crate::lineage::part_tool(b, "hole")).collect();
|
||||
apply_traced(doc, st, f, tool, Operation::Cut, std::slice::from_ref(&target))?;
|
||||
}
|
||||
@@ -2076,7 +2271,7 @@ pub(super) fn plastic_eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &F
|
||||
}
|
||||
let plane = Plane::from_normal(face.0, n).ok_or_else(|| DocError::Invalid("rim plane".into()))?;
|
||||
let tol = (mb.body.size() * 1e-3).max(1e-3);
|
||||
let mesh = mb.body.tessellate(tol)?;
|
||||
let mesh = mb.body.mesh(&solidcraft_kernel::MeshTolerance::chord(tol))?;
|
||||
let slice = FaceSlice { mesh: &mesh, plane, at: face.0, n, tol };
|
||||
let mut loops = slice.loops(0.0);
|
||||
loops.sort_by(|a, b| b.signed_area().abs().total_cmp(&a.signed_area().abs()));
|
||||
@@ -2099,7 +2294,7 @@ pub(super) fn plastic_eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &F
|
||||
[a, b, ..] => b.signed_area().abs() > 0.5 * a.signed_area().abs(),
|
||||
_ => false,
|
||||
};
|
||||
let (Some(outer), Some(inner), false) = (outer, openings.first().cloned(), alike) else {
|
||||
let (Some(_), Some(_), false) = (outer, openings.first(), alike) else {
|
||||
let what = match (loops.len(), openings.len()) {
|
||||
(0 | 1, _) => "pick the rim face of a shelled body (a face with an inner and an outer edge)".to_string(),
|
||||
(_, 0) => "pick the rim face of a shelled body: this face's inner edges are where other features stand on it, not an opening"
|
||||
@@ -2108,46 +2303,18 @@ pub(super) fn plastic_eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &F
|
||||
};
|
||||
return Err(DocError::Invalid(what));
|
||||
};
|
||||
let bw = if *groove { w + g } else { w };
|
||||
// A lip: the band raised from the rim's own faces (its walls continue the walls
|
||||
// below, which a join can't do: they would coincide).
|
||||
if !*groove
|
||||
&& let Ok(b) = kernel::raise_band(&mb.body, face.0, w, h, *outside)
|
||||
&& let Some(slot) = st.bodies.iter_mut().find(|x| x.name == target)
|
||||
{
|
||||
// A lip: the band raised from the rim's own faces, its walls continuing the walls below.
|
||||
if !*groove {
|
||||
let b = kernel::raise_band(&mb.body, face.0, w, h, *outside)?;
|
||||
let slot = st.bodies.iter_mut().find(|x| x.name == target).ok_or_else(|| DocError::Invalid("body".into()))?;
|
||||
*slot = ModelBody::traced_new(target.clone(), Traced::step(b, &[&mb.traced()]), slot.feature);
|
||||
return Ok(());
|
||||
}
|
||||
// The band along the chosen edge, inside the rim face. A groove's cutter reaches past
|
||||
// the edge into the free side (the cavity, or outside), so no wall coincides.
|
||||
let free = if *groove { bw } else { 0.0 };
|
||||
let (plane, band) = if let Ok(exact) = kernel::rim_band(&mb.body, face.0, bw, free, *outside) {
|
||||
exact
|
||||
} else if *outside {
|
||||
(
|
||||
plane,
|
||||
Region2 {
|
||||
outer: Loop2::polygon(&offset_left(&outer, -free)).ccw(),
|
||||
holes: vec![Loop2::polygon(&offset_left(&outer, bw)).ccw().reversed()],
|
||||
},
|
||||
)
|
||||
} else {
|
||||
(
|
||||
plane,
|
||||
Region2 {
|
||||
outer: Loop2::polygon(&offset_left(&inner, -bw)).ccw(),
|
||||
holes: vec![Loop2::polygon(&offset_left(&inner, free)).ccw().reversed()],
|
||||
},
|
||||
)
|
||||
};
|
||||
let delta = (h * 0.05).clamp(1e-3, 0.5);
|
||||
if *groove {
|
||||
let tool = kernel::extrude(&plane, &[band], -(h + g), delta)?.into_iter().map(|b| crate::lineage::part_tool(b, "groove")).collect();
|
||||
apply_traced(doc, st, f, tool, Operation::Cut, std::slice::from_ref(&target))
|
||||
} else {
|
||||
let tool = kernel::extrude(&plane, &[band], -delta, h)?.into_iter().map(|b| crate::lineage::part_tool(b, "lip")).collect();
|
||||
apply_traced(doc, st, f, tool, Operation::Join, std::slice::from_ref(&target))
|
||||
}
|
||||
// A groove: the band along the chosen edge, inside the rim face, its gap included,
|
||||
// cut down from the face.
|
||||
let (plane, band) = kernel::rim_band(&mb.body, face.0, w + g, 0.0, *outside)?;
|
||||
let tool = kernel::extrude(&plane, &[band], -(h + g), 0.0)?.into_iter().map(|b| crate::lineage::part_tool(b, "groove")).collect();
|
||||
apply_traced(doc, st, f, tool, Operation::Cut, std::slice::from_ref(&target))
|
||||
}
|
||||
FeatureKind::SnapFit { position, direction, hook, length, thickness, width, catch_depth, catch_length, base_height, new_body, body } => {
|
||||
let target = body_under(st, body, position.0)?;
|
||||
@@ -2159,11 +2326,9 @@ pub(super) fn plastic_eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &F
|
||||
return Err(DocError::Invalid("the snap fit's catch must be shorter than its arm".into()));
|
||||
}
|
||||
let (x, y, z) = local_frame(direction.0, Some(hook.0))?;
|
||||
let delta = (l * 0.05).clamp(1e-3, 0.5);
|
||||
// Side profile (x = toward the catch, y = up the arm), extruded across the width.
|
||||
// The base reaches `base_height` below the face; a joined arm bites `delta` into
|
||||
// the wall, a separate body sits flush on it.
|
||||
let y0 = if *new_body { -bh } else { -delta - bh };
|
||||
// The base reaches `base_height` below the face, joined or a separate body.
|
||||
let y0 = -bh;
|
||||
let pts = [Vec2::new(-t, y0), Vec2::new(0.0, y0), Vec2::new(0.0, l - cl), Vec2::new(cd, l - cl), Vec2::new(0.0, l), Vec2::new(-t, l)];
|
||||
let o = position.0 - y * (w / 2.0);
|
||||
let pl = Plane::new(o, x, z).ok_or_else(|| DocError::Invalid("snap fit frame".into()))?;
|
||||
@@ -2202,10 +2367,8 @@ pub(super) fn plastic_eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &F
|
||||
if closes {
|
||||
return Err(DocError::Invalid("the draft closes the rest before its top".into()));
|
||||
}
|
||||
let delta = (h * 0.05).clamp(1e-3, 0.5);
|
||||
let (x, y, z) = local_frame(direction.0, along.map(Vec3::from))?;
|
||||
// Sized at the face: the tool starts a little inside, grown by the draft over that.
|
||||
let e = delta * dr.tan();
|
||||
let (x, y, _) = local_frame(direction.0, along.map(Vec3::from))?;
|
||||
// Sized at the face, where the tool starts.
|
||||
let shape = |grow: f64| match l {
|
||||
Some(l) => Loop2::polygon(&[
|
||||
Vec2::new(-l / 2.0 - grow, -w / 2.0 - grow),
|
||||
@@ -2216,15 +2379,15 @@ pub(super) fn plastic_eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &F
|
||||
None => Loop2::circle(Vec2::ZERO, w / 2.0 + grow),
|
||||
};
|
||||
let holes = match t {
|
||||
Some(t) => vec![shape(-t - e).reversed()],
|
||||
Some(t) => vec![shape(-t).reversed()],
|
||||
None => Vec::new(),
|
||||
};
|
||||
let region = Region2 { outer: shape(e), holes };
|
||||
let base = Plane::new(position.0 - z * delta, x, y).ok_or_else(|| DocError::Invalid("rest frame".into()))?;
|
||||
let region = Region2 { outer: shape(0.0), holes };
|
||||
let base = Plane::new(position.0, x, y).ok_or_else(|| DocError::Invalid("rest frame".into()))?;
|
||||
let tool = if dr.abs() > 1e-12 {
|
||||
vec![kernel::extrude_tapered(&base, ®ion, h + delta, 1.0, -dr)?]
|
||||
vec![kernel::extrude_tapered(&base, ®ion, h, 1.0, -dr, kernel::DraftCorner::default())?]
|
||||
} else {
|
||||
kernel::extrude(&base, &[region], 0.0, h + delta)?
|
||||
kernel::extrude(&base, &[region], 0.0, h)?
|
||||
};
|
||||
let tool = tool.into_iter().map(|b| crate::lineage::part_tool(b, "rest")).collect();
|
||||
apply_traced(doc, st, f, tool, Operation::Join, std::slice::from_ref(&target))
|
||||
@@ -2254,6 +2417,13 @@ fn sheet_picks(sh: &crate::sheet::SheetBody, f: &Feature, edges: &[Vec3], warnin
|
||||
out
|
||||
}
|
||||
|
||||
/// The values of sheet `i`'s rule for that sheet: `Thickness` in them is the sheet's own
|
||||
/// thickness, which a converted body takes from its walls.
|
||||
fn sheet_rule_values(doc: &Document, vals: &BTreeMap<String, Value>, st: &ModelState, i: usize) -> Result<sheet::RuleValues> {
|
||||
let sh = st.sheets.get(i).ok_or_else(|| DocError::Invalid("sheet".into()))?;
|
||||
doc.rule_values_at(vals, &doc.sheet_rule(Some(&sh.rule)), Some(sh.t))
|
||||
}
|
||||
|
||||
/// Tag the flanges a feature just added (`F<feature>.<k>`).
|
||||
fn tag_flanges(sh: &mut crate::sheet::SheetBody, from: usize, feature: u64) {
|
||||
for (k, fl) in sh.flanges.iter_mut().enumerate().skip(from) {
|
||||
|
||||
@@ -20,6 +20,10 @@ pub(super) fn eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature,
|
||||
if !edges.is_empty() {
|
||||
let i = body_at(st, body, &crate::plain(edges))?;
|
||||
let b = st.bodies.get(i).ok_or_else(|| DocError::Invalid("body".into()))?;
|
||||
// A design may carry an order the fill does not build yet: refused, not built lower.
|
||||
for g in std::iter::once(continuity.all).chain(continuity.edges.iter().map(|e| e.g)) {
|
||||
g.built_by(kernel::Construction::Patch)?;
|
||||
}
|
||||
let spec = kernel::PatchSpec {
|
||||
continuity: continuity.all,
|
||||
edges: continuity.edges.iter().map(|e| (e.edge.0, e.g)).collect(),
|
||||
@@ -304,7 +308,7 @@ pub(super) fn eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature,
|
||||
st.sketch(sketch.0).ok_or_else(|| DocError::Unknown(format!("sketch {sketch} (it must come earlier in the timeline)")))?,
|
||||
f.component,
|
||||
)?;
|
||||
let (o, d) = revolve_axis(&ss, axis, &super::axis_state(doc, st, f.component, axis), &|name| {
|
||||
let (o, d) = revolve_axis(&ss, axis, &*super::axis_state(doc, st, f.component, axis)?, &|name| {
|
||||
construction_axis_in(Some(doc), st, name, f.component)
|
||||
})?;
|
||||
let a = val(vals, angle, Kind::Angle)?;
|
||||
@@ -371,17 +375,15 @@ pub(super) fn eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature,
|
||||
let pts: Vec<Vec2> = chains.iter().flatten().flat_map(|s| s.polyline(1e-2)).collect();
|
||||
let start = ss.plane.to_world(pts.iter().fold(Vec2::default(), |a, p| a + *p) * (1.0 / pts.len().max(1) as f64));
|
||||
let body = match path_points(ps, path, start)? {
|
||||
Some(pts) => {
|
||||
Some(path) => {
|
||||
let pts = path.points();
|
||||
let rails = crate::eval::rail_polylines(doc, st, rails, f.component, pts.first().copied().unwrap_or(start))?;
|
||||
let ctl = crate::eval::sweep_control(vals, orientation, twist, scale, rails, *rail_fit)?;
|
||||
kernel::sweep_sheet_points_with(&ss.plane, &chains, &pts, &ctl)?
|
||||
}
|
||||
None => {
|
||||
let segs = path_segments(ps, path, start)?;
|
||||
let path_start = match segs.first() {
|
||||
Some(kernel::PathSeg::Line { a, .. } | kernel::PathSeg::Arc { a, .. }) => *a,
|
||||
None => start,
|
||||
};
|
||||
let segs = path_segments_at(ps, path, start, Some(&ss.plane))?;
|
||||
let path_start = segs.first().map_or(start, kernel::PathSeg::start);
|
||||
let rails = crate::eval::rail_polylines(doc, st, rails, f.component, path_start)?;
|
||||
let ctl = crate::eval::sweep_control(vals, orientation, twist, scale, rails, *rail_fit)?;
|
||||
kernel::sweep_sheet_with(&ss.plane, &chains, &segs, &ctl)?
|
||||
@@ -459,7 +461,7 @@ pub(super) fn eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature,
|
||||
st.bodies.push(ModelBody::traced_new(name, made, f.id));
|
||||
Ok(())
|
||||
}
|
||||
FeatureKind::SurfaceRuled { body, edges, distance, mode, angle, direction, flip, corners, face } => {
|
||||
FeatureKind::SurfaceRuled { body, edges, distance, mode, angle, direction, flip, corners, face, far } => {
|
||||
let d = val(vals, distance, Kind::Length)?;
|
||||
let mode = match mode {
|
||||
RuledMode::Normal => kernel::RuledMode::Normal,
|
||||
@@ -478,7 +480,8 @@ pub(super) fn eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature,
|
||||
};
|
||||
let mb = st.body(body).cloned().ok_or_else(|| DocError::Unknown(format!("body `{body}`")))?;
|
||||
let r = kernel::Ruled { distance: d, mode, flip: *flip, corners, face: face.map(Vec3::from) };
|
||||
let made = Traced::step(kernel::ruled_surface(&mb.body, &crate::plain(edges), &r)?, &[&mb.traced()]);
|
||||
let handles: Vec<Option<Vec<Vec3>>> = far.iter().map(|h| h.as_ref().map(|pts| crate::plain(pts))).collect();
|
||||
let made = Traced::step(kernel::ruled_surface_with(&mb.body, &crate::plain(edges), &r, &handles)?, &[&mb.traced()]);
|
||||
let name = new_name(st, f, 0);
|
||||
st.bodies.push(ModelBody::traced_new(name, made, f.id));
|
||||
Ok(())
|
||||
@@ -611,7 +614,7 @@ fn network_sources(
|
||||
// 3D sketch curves are followed through their points; planar ones exactly.
|
||||
let mapped = path_in_frame(doc, raw, f.component)?;
|
||||
match path_points(&mapped, &c.curves, Vec3::new(f64::NAN, f64::NAN, f64::NAN))? {
|
||||
Some(pts) => NetSource::Points(pts),
|
||||
Some(path) => NetSource::Points(path.points()),
|
||||
None => {
|
||||
let ss = sketch_in_feature_frame(doc, raw, f.component)?;
|
||||
let mut chains = curve_chains(&ss, &c.curves)?.into_iter();
|
||||
|
||||
@@ -0,0 +1,883 @@
|
||||
//! Threads: the cosmetic thread on a cylindrical face, and the modelled
|
||||
//! thread's groove cut along a helix.
|
||||
|
||||
use super::*;
|
||||
use crate::thread_forms::{SteepFlank, ThreadForm, ThreadHand, ThreadTip};
|
||||
|
||||
/// A cosmetic thread: on which cylinder, its size, and the threaded part of the axis.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ThreadInfo {
|
||||
pub feature: u64,
|
||||
pub body: String,
|
||||
pub designation: String,
|
||||
pub major_diameter: f64,
|
||||
/// Radius of the actual cylindrical face when the cosmetic thread is evaluated (0 in a
|
||||
/// state saved without it: the cosmetic display then leaves the thread out).
|
||||
#[serde(default)]
|
||||
pub radius: f64,
|
||||
pub pitch: f64,
|
||||
pub internal: bool,
|
||||
pub axis_point: Vec3,
|
||||
pub axis: UnitVec3,
|
||||
/// Threaded part along the axis from `axis_point`.
|
||||
pub start: f64,
|
||||
pub end: f64,
|
||||
#[serde(default)]
|
||||
pub form: ThreadForm,
|
||||
#[serde(default)]
|
||||
pub hand: ThreadHand,
|
||||
#[serde(default = "crate::thread_forms::one_start")]
|
||||
pub starts: u32,
|
||||
}
|
||||
|
||||
/// Nominal diameters and coarse pitches (mm) used for automatic ISO metric thread sizing.
|
||||
/// Other diameter/pitch pairs can be supplied as explicit designations.
|
||||
pub fn metric_thread_sizes() -> &'static [(f64, f64)] {
|
||||
crate::thread_forms::ISO_COARSE
|
||||
}
|
||||
|
||||
/// "M8" or "M8x1" → (nominal diameter, pitch).
|
||||
pub fn parse_metric_thread(s: &str) -> Option<(f64, f64)> {
|
||||
ThreadForm::Metric.parse(s, 1).ok().filter(|z| (z.lead - z.pitch).abs() < 1e-12).map(|z| (z.major, z.pitch))
|
||||
}
|
||||
|
||||
/// The automatic ISO metric designation for a cylindrical face, using the same fit check as
|
||||
/// a thread feature without an explicit size. This query does not change the model.
|
||||
pub fn suggested_thread(st: &ModelState, p: Vec3) -> Result<String> {
|
||||
suggested_thread_in(st, p, ThreadForm::Metric)
|
||||
}
|
||||
|
||||
/// The standard designation of `form` that fits the cylindrical face at `p` (a shaft its major
|
||||
/// diameter, a hole its drill), with the same fit check as a thread feature without an explicit
|
||||
/// size. This query does not change the model.
|
||||
pub fn suggested_thread_in(st: &ModelState, p: Vec3, form: ThreadForm) -> Result<String> {
|
||||
thread_on(st, 0, p, &ThreadAsk { form: Some(form), ..Default::default() }).map(|t| t.info.designation)
|
||||
}
|
||||
|
||||
/// How far a thread runs along its face.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
pub(super) enum Extent {
|
||||
/// The whole face.
|
||||
#[default]
|
||||
Full,
|
||||
/// This long, from the end it starts at.
|
||||
Length(f64),
|
||||
/// From the end it starts at to where this plane crosses the axis.
|
||||
UpTo(Plane),
|
||||
/// From the end it starts at to this point's place along the axis.
|
||||
UpToPoint(Vec3),
|
||||
}
|
||||
|
||||
/// What a thread feature asks for: its size, how far it runs and from which end.
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub(super) struct ThreadAsk<'a> {
|
||||
/// The form; `None`: the one `designation`'s prefix names, else metric.
|
||||
pub form: Option<ThreadForm>,
|
||||
pub designation: Option<&'a str>,
|
||||
pub hand: ThreadHand,
|
||||
/// Starts; 0 is taken as 1.
|
||||
pub starts: u32,
|
||||
pub steep: SteepFlank,
|
||||
pub extent: Extent,
|
||||
/// A point near the end of the face the thread starts at.
|
||||
pub from: Option<Vec3>,
|
||||
/// A modelled thread needs to know how each of its ends meets the face's ends.
|
||||
pub modeled: bool,
|
||||
/// The runout at the end away from where the thread starts, as (mm, pitches): its length
|
||||
/// is mm + pitches x the pitch. The start never fades: it runs off its free end.
|
||||
pub ease_out: (f64, f64),
|
||||
/// A tapped hole's thread: `designation` is a catalogue size (ISO metric, or a catalogued
|
||||
/// UNC/UNF, G, Rp, Rc or NPT size), and a size that is not metric fits the hole drilled for it.
|
||||
pub tapped: bool,
|
||||
}
|
||||
|
||||
/// How one end of a thread's length meets its face.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum End {
|
||||
/// At an end of the face past which there is no material in the groove's way: the groove
|
||||
/// runs out into the air (or through a chamfer).
|
||||
Free,
|
||||
/// At (or within half a pitch of) an end of the face where material goes on past it, a
|
||||
/// shoulder, a head, a blind hole's bottom: the groove stops short, clear of it.
|
||||
Closed,
|
||||
/// Inside the face: the groove stops square there.
|
||||
Inside,
|
||||
}
|
||||
|
||||
/// A thread resolved on its face.
|
||||
pub(super) struct ThreadOn {
|
||||
pub info: ThreadInfo,
|
||||
cyl: kernel::CylinderFace,
|
||||
/// How the thread's start and end meet the face (modelled threads only).
|
||||
ends: [End; 2],
|
||||
steep: SteepFlank,
|
||||
/// Runout lengths (mm) at the thread's start and end along the axis.
|
||||
ease: [f64; 2],
|
||||
/// The end of the axis (0: its start, 1: its end) the thread starts from.
|
||||
from: usize,
|
||||
}
|
||||
|
||||
/// The free-end test of a face's two ends (`side` 0 at its start, 1 at its end), each made
|
||||
/// once. The groove's one-pitch overrun past a free end reaches 1.5 pitches past it. An end is
|
||||
/// closed when, within that reach, material lies outside the face's cylinder (inside it, for a
|
||||
/// hole) at the radii the groove cuts beyond the face: a shoulder, a plate, a head, a blind
|
||||
/// hole's bottom, however thin, the material stepping out past the face. It is closed too when
|
||||
/// material lies in the groove's band of radii beyond its first pitch: the face's cylinder going
|
||||
/// on past a split. A chamfer, a neck, a countersink or a counterbore steps the other way and
|
||||
/// leaves the end free: the groove runs out through it.
|
||||
struct FaceEnds<'a> {
|
||||
body: &'a Body,
|
||||
cyl: &'a kernel::CylinderFace,
|
||||
band: (f64, f64),
|
||||
/// The radii the groove cuts beyond the face's own: from it out to the groove's outer edge
|
||||
/// (external), or in to its inner edge (internal).
|
||||
beyond: (f64, f64),
|
||||
pitch: f64,
|
||||
free: [Option<bool>; 2],
|
||||
}
|
||||
|
||||
impl FaceEnds<'_> {
|
||||
fn free(&mut self, side: usize) -> Result<bool> {
|
||||
if let Some(Some(f)) = self.free.get(side) {
|
||||
return Ok(*f);
|
||||
}
|
||||
let p = self.pitch;
|
||||
let past =
|
||||
|from: f64, to: f64| if side == 0 { (self.cyl.start - to, self.cyl.start - from) } else { (self.cyl.end + from, self.cyl.end + to) };
|
||||
// Material that only touches the band's rims (a counterbore as wide as the thread, the
|
||||
// face's own end) is not in the groove's way.
|
||||
let e = p * 1e-3;
|
||||
let stepped_out = kernel::material_in_ring(self.body, self.cyl, self.beyond.0 + e, self.beyond.1 - e, past(e, 1.5 * p))?;
|
||||
let free = !stepped_out && !kernel::material_in_ring(self.body, self.cyl, self.band.0 + e, self.band.1 - e, past(p, 1.5 * p))?;
|
||||
if let Some(slot) = self.free.get_mut(side) {
|
||||
*slot = Some(free);
|
||||
}
|
||||
Ok(free)
|
||||
}
|
||||
}
|
||||
|
||||
/// A thread on the cylindrical face at `p`: its size, and the part of the face it runs over.
|
||||
pub(super) fn thread_on(st: &ModelState, feature: u64, p: Vec3, ask: &ThreadAsk) -> Result<ThreadOn> {
|
||||
let (bi, cyl) = st
|
||||
.bodies
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, b)| {
|
||||
#[cfg(test)]
|
||||
super::cost_tests::count(|c| c.cylinder_search += 1);
|
||||
kernel::cylinder_face_at(&b.body, p).map(|c| (i, c))
|
||||
})
|
||||
.min_by(|a, b| {
|
||||
let dist = |c: &kernel::CylinderFace| ((p - c.axis_point - c.axis * (p - c.axis_point).dot(c.axis)).len() - c.radius).abs();
|
||||
dist(&a.1).total_cmp(&dist(&b.1))
|
||||
})
|
||||
.ok_or_else(|| DocError::Invalid("no cylindrical face there to thread".into()))?;
|
||||
let mb = st.bodies.get(bi).ok_or_else(|| DocError::Invalid("threaded body".into()))?;
|
||||
let dia = cyl.radius * 2.0;
|
||||
let form = match (ask.form, ask.designation.and_then(ThreadForm::of_designation)) {
|
||||
(Some(f), _) | (None, Some(f)) => f,
|
||||
(None, None) => ThreadForm::Metric,
|
||||
};
|
||||
if let Some(why) = form.refused() {
|
||||
return Err(DocError::Invalid(why.into()));
|
||||
}
|
||||
let starts = ask.starts.max(1);
|
||||
if starts > 8 {
|
||||
return Err(DocError::Invalid("a thread has 1 to 8 starts".into()));
|
||||
}
|
||||
// A tapped hole's size is the catalogue's.
|
||||
let tapped = match (ask.tapped, ask.designation) {
|
||||
(false, _) => None,
|
||||
(true, None) => return Err(DocError::Invalid("a tapped hole needs a designation".into())),
|
||||
(true, Some(name)) => Some(crate::parse_tapped_thread(name).ok_or_else(|| {
|
||||
DocError::Invalid(format!("unknown tapped thread `{name}` (ISO metric or a catalogued UNC/UNF, G, Rp, Rc or NPT size)"))
|
||||
})?),
|
||||
};
|
||||
let size = match (&tapped, ask.designation) {
|
||||
(Some(t), _) => crate::thread_forms::ThreadSize { form, major: t.major_diameter, pitch: t.pitch, lead: t.pitch },
|
||||
(None, Some(s)) => form.parse(s, starts).map_err(DocError::Invalid)?,
|
||||
(None, None) => {
|
||||
let (d, p) = form.fit(dia, cyl.internal).ok_or_else(|| {
|
||||
DocError::Invalid(if form.sizes().is_empty() {
|
||||
format!("{} threads have no size table: give the designation ({})", form.name(), form.format(50.8, 25.4 / 8.0, 25.4 / 8.0))
|
||||
} else {
|
||||
format!("no {} thread fits a {dia:.2} mm cylinder", form.name())
|
||||
})
|
||||
})?;
|
||||
crate::thread_forms::ThreadSize { form, major: d, pitch: p, lead: p * f64::from(starts) }
|
||||
}
|
||||
};
|
||||
let (major, pitch) = (size.major, size.pitch);
|
||||
// A designation that gives the lead (M16xPh3P1.5, Tr40x14(P7)) gives the starts.
|
||||
let named = (size.lead / pitch).round() as u32;
|
||||
if ask.starts > 1 && named != starts {
|
||||
return Err(DocError::Invalid(format!("`{}` has {named} start(s), not {starts}", ask.designation.unwrap_or_default())));
|
||||
}
|
||||
let starts = named.max(1);
|
||||
let mut designation = tapped.as_ref().map(|t| t.designation.clone()).unwrap_or_else(|| form.format(major, pitch, size.lead));
|
||||
if !form.pipe_sizes().is_empty() && tapped.is_none() {
|
||||
if ask.hand == ThreadHand::Both {
|
||||
return Err(DocError::Invalid("a pipe thread turns one way: right or left".into()));
|
||||
}
|
||||
// ISO 7-1's Rp is the parallel internal thread an R screws into; there is no external Rp.
|
||||
if form == ThreadForm::Rp && !cyl.internal {
|
||||
return Err(DocError::Invalid(format!(
|
||||
"`{designation}` on this shaft: Rp is an internal thread (ISO 7-1); an external pipe thread is R (taper, ISO 7-1) or G (ISO 228-1)"
|
||||
)));
|
||||
}
|
||||
// ISO 7-1 names the internal taper thread Rc and the external one R.
|
||||
if form == ThreadForm::Bspt {
|
||||
if let Some(given) = ask.designation
|
||||
&& ThreadForm::pipe_internal(given) != cyl.internal
|
||||
{
|
||||
let want = if cyl.internal { "an internal taper thread is Rc" } else { "an external taper thread is R" };
|
||||
return Err(DocError::Invalid(format!("`{given}` on this {}: {want} (ISO 7-1)", if cyl.internal { "hole" } else { "shaft" })));
|
||||
}
|
||||
if cyl.internal {
|
||||
designation = designation.replacen("R ", "Rc ", 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
let minor = 2.0 * form.groove(major, pitch, true).crest_r;
|
||||
let fits = if let Some(t) = tapped.as_ref().filter(|t| t.family != crate::TappedThreadFamily::Metric) {
|
||||
// The hole drilled for it, without borrowing an ISO minor-diameter rule.
|
||||
cyl.internal && (t.tap_drill - dia).abs() < 0.15 * major
|
||||
} else if cyl.internal {
|
||||
(minor - dia).abs() < 0.2 * major || (form.drill(major, pitch) - dia).abs() < 0.15 * major
|
||||
} else {
|
||||
(major - dia).abs() < 0.15 * major
|
||||
};
|
||||
if !fits {
|
||||
return Err(DocError::Invalid(format!("an {designation} thread does not fit a {dia:.2} mm {}", if cyl.internal { "hole" } else { "shaft" })));
|
||||
}
|
||||
// The radii the groove cuts: from the external thread's minor diameter (a nut's: its minor)
|
||||
// to the major (a nut's root), and to the face itself where it lies outside that.
|
||||
let (r_lo, r_hi) = if cyl.internal {
|
||||
let g = form.groove(major, pitch, true);
|
||||
(g.crest_r, g.root_r)
|
||||
} else {
|
||||
(form.groove(major, pitch, false).root_r, major / 2.0)
|
||||
};
|
||||
let band = (cyl.radius.min(r_lo), cyl.radius.max(r_hi));
|
||||
// As far beyond the face as the groove's section reaches ([`thread_grooves`]).
|
||||
let beyond =
|
||||
if cyl.internal { ((cyl.radius.min(r_lo) - 0.1 * pitch).max(0.0), cyl.radius) } else { (cyl.radius, cyl.radius.max(r_hi) + 0.05 * pitch) };
|
||||
let mut ends = FaceEnds { body: &mb.body, cyl: &cyl, band, beyond, pitch, free: [None; 2] };
|
||||
let (lo, hi) = (cyl.start, cyl.end);
|
||||
let along = |q: Vec3| (q - cyl.axis_point).dot(cyl.axis);
|
||||
let span = |side: usize, to: f64| -> Result<(f64, f64)> {
|
||||
let (a, b) = if side == 0 { (lo, to.min(hi)) } else { (to.max(lo), hi) };
|
||||
if !(b - a > pitch * 1e-3) {
|
||||
return Err(DocError::Invalid("the thread's limit is not along the face from the end it starts at".into()));
|
||||
}
|
||||
Ok((a, b))
|
||||
};
|
||||
let from_side = |ends: &mut FaceEnds| start_side(ends, ask.from.map(along), along(p));
|
||||
// An external taper thread runs its standard length (ISO 7-1's useful thread, NPT's
|
||||
// effective thread L2) unless told otherwise; an internal one the face, as a tapped hole.
|
||||
let extent = match (ask.extent, form.is_taper() && !cyl.internal) {
|
||||
(Extent::Full, true) => form.pipe_size(major).map_or(Extent::Full, |z| Extent::Length(z.length)),
|
||||
(e, _) => e,
|
||||
};
|
||||
let (a, b, side) = match extent {
|
||||
Extent::Full => (lo, hi, ask.from.map(|q| usize::from((along(q) - lo).abs() > (hi - along(q)).abs())).unwrap_or(0)),
|
||||
Extent::Length(l) => {
|
||||
if !(l > 0.0) {
|
||||
return Err(DocError::Invalid("thread length must be positive".into()));
|
||||
}
|
||||
if l >= hi - lo - pitch * 1e-3 {
|
||||
// As long as the face (or longer): it runs the whole face from either end.
|
||||
(lo, hi, ask.from.map(|q| usize::from((along(q) - lo).abs() > (hi - along(q)).abs())).unwrap_or(0))
|
||||
} else {
|
||||
let side = from_side(&mut ends)?;
|
||||
let (a, b) = span(side, if side == 0 { lo + l } else { hi - l })?;
|
||||
(a, b, side)
|
||||
}
|
||||
}
|
||||
Extent::UpTo(pl) => {
|
||||
let n = pl.normal();
|
||||
let k = n.dot(*cyl.axis);
|
||||
if k.abs() < 1e-9 {
|
||||
return Err(DocError::Invalid("the plane the thread runs up to is parallel to its axis".into()));
|
||||
}
|
||||
let to = n.dot(pl.origin - cyl.axis_point) / k;
|
||||
let side = from_side(&mut ends)?;
|
||||
let (a, b) = span(side, to)?;
|
||||
(a, b, side)
|
||||
}
|
||||
Extent::UpToPoint(q) => {
|
||||
let side = from_side(&mut ends)?;
|
||||
let (a, b) = span(side, along(q))?;
|
||||
(a, b, side)
|
||||
}
|
||||
};
|
||||
// The runout at each end of the axis: only at the end away from where it starts.
|
||||
let e_out = ask.ease_out.0 + ask.ease_out.1 * pitch;
|
||||
let ease = if side == 0 { [0.0, e_out] } else { [e_out, 0.0] };
|
||||
if ease.iter().any(|e| !(*e >= 0.0 && e.is_finite())) {
|
||||
return Err(DocError::Invalid("a runout length must be positive".into()));
|
||||
}
|
||||
// How each end of the thread meets the face: at a face end, or within half a pitch (and its
|
||||
// runout) of one, where the groove would reach past it, that end's free-end test decides.
|
||||
let near = |at: f64, side: usize| if side == 0 { at - lo } else { hi - at } < pitch / 2.0 + ease.get(side).copied().unwrap_or(0.0);
|
||||
let mut kind = |at: f64, side: usize| -> Result<End> {
|
||||
Ok(if !ask.modeled || !near(at, side) {
|
||||
End::Inside
|
||||
} else if !ends.free(side)? {
|
||||
End::Closed
|
||||
} else if (if side == 0 { at - lo } else { hi - at }) < pitch * 1e-3 {
|
||||
End::Free
|
||||
} else {
|
||||
End::Inside
|
||||
})
|
||||
};
|
||||
let ends = [kind(a, 0)?, kind(b, 1)?];
|
||||
if form.is_taper() && ask.modeled && ends.get(side) != Some(&End::Free) {
|
||||
return Err(DocError::Invalid(format!(
|
||||
"a taper pipe thread starts at a free end, the pipe's end or the {}: start it there (`from`, a point near that end)",
|
||||
if cyl.internal { "hole's mouth" } else { "shaft's end" }
|
||||
)));
|
||||
}
|
||||
let info = ThreadInfo {
|
||||
feature,
|
||||
body: mb.name.clone(),
|
||||
designation,
|
||||
major_diameter: major,
|
||||
radius: cyl.radius,
|
||||
pitch,
|
||||
internal: cyl.internal,
|
||||
axis_point: cyl.axis_point,
|
||||
axis: cyl.axis,
|
||||
start: a,
|
||||
end: b,
|
||||
form,
|
||||
hand: ask.hand,
|
||||
starts,
|
||||
};
|
||||
Ok(ThreadOn { info, cyl, ends, steep: ask.steep, ease, from: side })
|
||||
}
|
||||
|
||||
/// The end of the face (0: its start, 1: its end) a thread of limited length starts at: the end
|
||||
/// nearest `from` (an axial position) when given, else the face's only free end, else the end the
|
||||
/// pick `at` lies near (within a third of the face). A pick in the middle of a face whose ends are
|
||||
/// alike is refused rather than guessed.
|
||||
fn start_side(ends: &mut FaceEnds, from: Option<f64>, at: f64) -> Result<usize> {
|
||||
let (lo, hi) = (ends.cyl.start, ends.cyl.end);
|
||||
if let Some(s) = from {
|
||||
return match (s - lo).abs().total_cmp(&(hi - s).abs()) {
|
||||
std::cmp::Ordering::Less => Ok(0),
|
||||
std::cmp::Ordering::Greater => Ok(1),
|
||||
std::cmp::Ordering::Equal => Err(DocError::Invalid("`from` is as near one end of the face as the other".into())),
|
||||
};
|
||||
}
|
||||
match (ends.free(0)?, ends.free(1)?) {
|
||||
(true, false) => return Ok(0),
|
||||
(false, true) => return Ok(1),
|
||||
_ => {}
|
||||
}
|
||||
let third = (hi - lo) / 3.0;
|
||||
if at - lo < third {
|
||||
Ok(0)
|
||||
} else if hi - at < third {
|
||||
Ok(1)
|
||||
} else {
|
||||
let alike = if ends.free(0)? { "both ends of the face are free" } else { "material goes on past both ends of the face" };
|
||||
Err(DocError::Invalid(format!("{alike}: say which end the thread starts from (`from`, a point near that end)")))
|
||||
}
|
||||
}
|
||||
|
||||
/// A thread's groove as a screw sweep: its `section` in `plane` (an axial plane through `base`),
|
||||
/// carried `turns` turns about `axis` through `base`, rising `pitch` per turn (negative: a
|
||||
/// left-handed groove).
|
||||
struct GrooveShape {
|
||||
plane: Plane,
|
||||
section: Vec<Vec2>,
|
||||
base: Vec3,
|
||||
axis: Vec3,
|
||||
pitch: f64,
|
||||
turns: f64,
|
||||
/// The section's move off the axis (turn, mm), piecewise linear: the runouts. Empty: none.
|
||||
law: Vec<(f64, f64)>,
|
||||
}
|
||||
|
||||
/// The helical spaces between the teeth of a modelled thread of the form's profile: cut from
|
||||
/// the shaft (external) or the hole wall (internal), running from `start` to `end` along the
|
||||
/// axis, one groove per start and hand. `radius` is the face's own radius, `face` its extent
|
||||
/// along the axis and `ends` how the thread's two ends meet it. A taper thread's `taper` is
|
||||
/// (its major diameter where it starts, the end of the axis it starts from): its diameters grow
|
||||
/// 1 in 16 from there into an external thread's material and shrink into an internal one's.
|
||||
fn thread_grooves(
|
||||
t: &ThreadInfo,
|
||||
radius: f64,
|
||||
face: (f64, f64),
|
||||
ends: [End; 2],
|
||||
steep: SteepFlank,
|
||||
ease: [f64; 2],
|
||||
taper: Option<(f64, usize)>,
|
||||
) -> Result<Vec<GrooveShape>> {
|
||||
let (p, d, form) = (t.pitch, t.major_diameter, t.form);
|
||||
let lead = p * f64::from(t.starts.max(1));
|
||||
let len = t.end - t.start;
|
||||
if !(p > 1e-3 && len > 1e-6) {
|
||||
return Err(DocError::Invalid("thread too short".into()));
|
||||
}
|
||||
if len / lead > 190.0 {
|
||||
return Err(DocError::Invalid(format!(
|
||||
"a modelled thread is limited to 190 turns ({:.0} mm at this lead); use a cosmetic one",
|
||||
190.0 * lead
|
||||
)));
|
||||
}
|
||||
// The major diameter at axial position z: fixed, or a taper's from where it starts.
|
||||
let major_at = |z: f64| match taper {
|
||||
Some((d0, from)) => {
|
||||
let (z0, into) = if from == 0 { (t.start, 1.0) } else { (t.end, -1.0) };
|
||||
let grow = if t.internal { -1.0 } else { 1.0 };
|
||||
d0 + grow * into * (z - z0) / 16.0
|
||||
}
|
||||
None => d,
|
||||
};
|
||||
let d = taper.map_or(d, |(d0, _)| d0);
|
||||
let profile = |d: f64| {
|
||||
let mut g = form.groove(d, p, t.internal);
|
||||
if steep == SteepFlank::Start {
|
||||
std::mem::swap(&mut g.flank_lo, &mut g.flank_hi);
|
||||
}
|
||||
g
|
||||
};
|
||||
let g = profile(d);
|
||||
let alt = |dia: f64| form.fit(dia, t.internal).map(|(n, q)| form.format(n, q, q)).filter(|a| *a != t.designation);
|
||||
// How deep the groove cuts into the material: from the hole wall out to the internal
|
||||
// thread's root, or from the shaft's surface in to the external thread's root. Next to
|
||||
// nothing (a hole as wide as the thread, #995) is refused: the cut would leave the face
|
||||
// unthreaded.
|
||||
let basic = g.basic_depth;
|
||||
let depth = if t.internal { g.root_r - radius } else { radius - g.root_r };
|
||||
let drill = show_mm(form.drill(d, p));
|
||||
if !(depth >= basic / 4.0) {
|
||||
let (m, dia) = (&t.designation, 2.0 * radius);
|
||||
let cut = format!("it would cut {:.2} mm of the thread's {basic:.2} mm depth", depth.max(0.0));
|
||||
let shown = show_mm(dia);
|
||||
return Err(DocError::Invalid(if t.internal {
|
||||
let alt = alt(dia).map(|a| format!(", or thread this hole {a}")).unwrap_or_default();
|
||||
format!("this {shown} mm hole is too wide for a modelled {m} thread: {cut}; drill {drill} mm for {m}{alt}")
|
||||
} else {
|
||||
let alt = alt(dia).map(|a| format!(", or thread this shaft {a}")).unwrap_or_default();
|
||||
format!("this {shown} mm shaft is too thin for a modelled {m} thread: {cut}; make the shaft {} mm for {m}{alt}", show_mm(d))
|
||||
}));
|
||||
}
|
||||
// The section: from the root out past the surface (external), or from inside the hole wall
|
||||
// out to the root (internal), so the cut leaves no skin, its flanks the groove's.
|
||||
// A taper thread's crests are cut by its major cone (external) or minor cone (internal),
|
||||
// as the die or tap cuts them ([`taper_envelope`]): its groove reaches past the crest by as
|
||||
// much as narrows the tooth's crest flat by a quarter (NPT's flat is 0.038 P). The cone
|
||||
// changes 1 in 32 along the axis (`major_at`) across the section's own width, so its edge
|
||||
// there follows the cone at that margin rather than being cut square: square, its deeper
|
||||
// corner stops short of the cone and leaves a sliver of each flank.
|
||||
let cone = taper.map_or(0.0, |_| (major_at(t.start + 1.0) - major_at(t.start)) / 2.0);
|
||||
let section_of = |g: &crate::thread_forms::GrooveProfile| -> (Vec<Vec2>, f64) {
|
||||
let (tl, th) = (g.flank_lo.tan(), g.flank_hi.tan());
|
||||
if taper.is_none() {
|
||||
let (r0, r1) = if t.internal { (radius.min(g.crest_r) - 0.1 * p, g.root_r) } else { (g.root_r, radius.max(d / 2.0) + 0.05 * p) };
|
||||
let ((lo0, hi0), (lo1, hi1)) = (g.extents(p, r0), g.extents(p, r1));
|
||||
let wide = g.width(p, if t.internal { r0 } else { r1 });
|
||||
return (vec![Vec2::new(r0, -lo0), Vec2::new(r1, -lo1), Vec2::new(r1, hi1), Vec2::new(r0, hi0)], wide);
|
||||
}
|
||||
// Depth from the root, out towards the crest: d(z) = dc + q·z along the edge, which
|
||||
// meets the flanks z = −(h + d·tl) and z = h + d·th.
|
||||
let o = if t.internal { -1.0 } else { 1.0 };
|
||||
let q = o * cone;
|
||||
let h = g.width(p, g.root_r) / 2.0;
|
||||
let dc = o * (g.crest_r - g.root_r) + 0.25 * g.crest_flat / (tl + th);
|
||||
let (z_lo, z_hi) = (-(h + dc * tl) / (1.0 + q * tl), (h + dc * th) / (1.0 - q * th));
|
||||
let r = |z: f64| g.root_r + o * (dc + q * z);
|
||||
let (root, edge) = ([Vec2::new(g.root_r, -h), Vec2::new(g.root_r, h)], [Vec2::new(r(z_lo), z_lo), Vec2::new(r(z_hi), z_hi)]);
|
||||
// In the order the square section has: the inner radius's corners first.
|
||||
let pts = if t.internal { vec![edge[0], root[0], root[1], edge[1]] } else { vec![root[0], edge[0], edge[1], root[1]] };
|
||||
(pts, z_hi - z_lo)
|
||||
};
|
||||
let (mut section, wide) = section_of(&g);
|
||||
let radii = |s: &[Vec2]| s.iter().fold((f64::INFINITY, f64::NEG_INFINITY), |(a, b), v| (a.min(v.x), b.max(v.x)));
|
||||
let (r0, r1) = radii(§ion);
|
||||
if wide >= p * 0.98 {
|
||||
// The groove would be wider than a pitch: the face is far inside the thread (a hole
|
||||
// narrower than its minor diameter, a shaft thicker than its major one, #2688).
|
||||
let (m, dia) = (&t.designation, 2.0 * radius);
|
||||
let shown = show_mm(dia);
|
||||
return Err(DocError::Invalid(if t.internal {
|
||||
let alt = alt(dia).map(|a| format!(", or thread this hole {a}")).unwrap_or_default();
|
||||
format!("this {shown} mm hole is too narrow for a modelled {m} thread (its teeth would fill the hole); drill {drill} mm for {m}{alt}")
|
||||
} else {
|
||||
let alt = alt(dia).map(|a| format!(", or thread this shaft {a}")).unwrap_or_default();
|
||||
format!("this {shown} mm shaft is too thick for a modelled {m} thread; make the shaft {} mm for {m}{alt}", show_mm(d))
|
||||
}));
|
||||
}
|
||||
if !(r0.is_finite() && r1.is_finite() && r0 > 0.0) {
|
||||
return Err(DocError::Invalid("thread dimensions are out of range".into()));
|
||||
}
|
||||
// Past a free end of the face the groove runs out into the air, a pitch beyond it. Before
|
||||
// a closed one it stops with its section clear of the face's end (its centre as far inside
|
||||
// as the section reaches that way), so it never cuts into the shoulder there; inside the
|
||||
// face it stops square at the threaded length's end.
|
||||
//
|
||||
// A runout `e` at an end (ISO 3508) fades the groove out over `e`: its section moves out of
|
||||
// the material (an external thread's away from the axis, past the surface; an internal
|
||||
// one's towards it, into the hole) as it turns. It lies past the full-form length, up to the
|
||||
// shoulder's clearance; at a free end, inside the face, the groove reaching nothing there.
|
||||
// How far the section reaches each way along the axis.
|
||||
let (w_lo, w_hi) = section.iter().fold((0.0_f64, 0.0_f64), |(a, b), v| (a.max(-v.y), b.max(v.y)));
|
||||
let s0 = match (ends[0], ease[0] > 0.0) {
|
||||
(End::Free, false) => t.start - p,
|
||||
(End::Free, true) => t.start,
|
||||
(End::Closed, _) => (t.start - ease[0]).max(face.0 + w_lo),
|
||||
(End::Inside, _) => t.start - ease[0],
|
||||
};
|
||||
let s1 = match (ends[1], ease[1] > 0.0) {
|
||||
(End::Free, false) => t.end + p,
|
||||
(End::Free, true) => t.end,
|
||||
(End::Closed, _) => (t.end + ease[1]).min(face.1 - w_hi),
|
||||
(End::Inside, _) => t.end + ease[1],
|
||||
};
|
||||
if !(s1 - s0 > p * 1e-3) {
|
||||
return Err(DocError::Invalid(format!(
|
||||
"the thread is too short to model between the shoulders: its groove needs {} mm clear of each",
|
||||
show_mm(w_lo.max(w_hi))
|
||||
)));
|
||||
}
|
||||
let turns = (s1 - s0) / lead;
|
||||
// A taper thread's section is the profile where the sweep begins, moved off the axis as it
|
||||
// turns by the taper's 1/32 of the axial travel.
|
||||
let mut r0 = r0;
|
||||
let taper_rate = match taper {
|
||||
Some(_) => {
|
||||
section = section_of(&profile(major_at(s0))).0;
|
||||
r0 = radii(§ion).0;
|
||||
(major_at(s0 + lead) - major_at(s0)) / 2.0
|
||||
}
|
||||
None => 0.0,
|
||||
};
|
||||
// How far the section moves to leave the material, a little past it.
|
||||
let out = if t.internal { -(r1 - radius + 0.05 * p) } else { radius - r0 + 0.05 * p };
|
||||
if t.internal && r0 + out < 0.1 * p {
|
||||
return Err(DocError::Invalid("the hole is too small for this thread's runout".into()));
|
||||
}
|
||||
let (r_in, r_out) = (ease[0] / lead, ease[1] / lead);
|
||||
let mut law = Vec::new();
|
||||
if r_in > 0.0 {
|
||||
law.extend([(0.0, out), (r_in, 0.0)]);
|
||||
}
|
||||
if r_out > 0.0 {
|
||||
law.extend([(turns - r_out, 0.0), (turns, out)]);
|
||||
}
|
||||
if r_in > 0.0 && r_out > 0.0 && !(turns - r_out > r_in + 1e-9) || r_in.max(r_out) > turns {
|
||||
return Err(DocError::Invalid(format!("the runout ({} mm) is longer than the thread", show_mm(ease[0].max(ease[1])))));
|
||||
}
|
||||
if taper_rate != 0.0 {
|
||||
law = with_taper(&law, turns, taper_rate);
|
||||
if t.internal && law.iter().any(|(_, o)| r0 + o < 0.1 * p) {
|
||||
return Err(DocError::Invalid("the hole is too small for this taper thread".into()));
|
||||
}
|
||||
}
|
||||
let k = *t.axis;
|
||||
let u = k.cross(if k.x.abs() < 0.9 { Vec3::new(1.0, 0.0, 0.0) } else { Vec3::new(0.0, 1.0, 0.0) });
|
||||
let u = u.normalized().ok_or_else(|| DocError::Invalid("thread axis".into()))?;
|
||||
let base = t.axis_point + k * s0;
|
||||
let hands: &[f64] = match t.hand {
|
||||
ThreadHand::Right => &[1.0],
|
||||
ThreadHand::Left => &[-1.0],
|
||||
ThreadHand::Both => &[1.0, -1.0],
|
||||
};
|
||||
let n = t.starts.max(1);
|
||||
let mut grooves = Vec::new();
|
||||
for &h in hands {
|
||||
for j in 0..n {
|
||||
// Start j is the first turned a j-th of a turn on: the same helix a j-th of the
|
||||
// lead along.
|
||||
let a = std::f64::consts::TAU * f64::from(j) / f64::from(n);
|
||||
let x = u * a.cos() + k.cross(u) * a.sin();
|
||||
// A left-handed groove: turning the same way about the reversed axis while rising
|
||||
// along it (the screw motion about −k rising −lead per turn).
|
||||
let (axis, pitch) = if h > 0.0 { (k, lead) } else { (-k, -lead) };
|
||||
grooves.push(GrooveShape {
|
||||
plane: Plane { origin: base, x, y: k },
|
||||
section: section.clone(),
|
||||
base,
|
||||
axis,
|
||||
pitch,
|
||||
turns,
|
||||
law: law.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(grooves)
|
||||
}
|
||||
|
||||
/// A radial law (turn, offset) with a taper's `rate` mm per turn added: the law's own points and
|
||||
/// both ends, each offset the law's (held past its first and last points) plus the taper's.
|
||||
fn with_taper(law: &[(f64, f64)], turns: f64, rate: f64) -> Vec<(f64, f64)> {
|
||||
let at = |t: f64| -> f64 {
|
||||
match (law.first(), law.last()) {
|
||||
(Some(a), _) if t <= a.0 => a.1,
|
||||
(_, Some(b)) if t >= b.0 => b.1,
|
||||
_ => law
|
||||
.windows(2)
|
||||
.find_map(|w| match w {
|
||||
[a, b] if t >= a.0 && t <= b.0 && b.0 > a.0 => Some(a.1 + (b.1 - a.1) * (t - a.0) / (b.0 - a.0)),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or(0.0),
|
||||
}
|
||||
};
|
||||
let mut ts: Vec<f64> = law.iter().map(|(t, _)| *t).chain([0.0, turns]).collect();
|
||||
ts.sort_by(f64::total_cmp);
|
||||
ts.dedup_by(|a, b| (*a - *b).abs() < 1e-12);
|
||||
ts.into_iter().map(|t| (t, at(t) + rate * t)).collect()
|
||||
}
|
||||
|
||||
/// A taper thread's crests, as its die or tap cuts them: the ring of material outside its major
|
||||
/// cone (external) or inside its minor cone (internal) along the thread, from a pitch past the
|
||||
/// end it starts at (`from`, a free end; `d0` its major diameter there) to its other end (a pitch
|
||||
/// past that too when it is free). None when the cone misses the material.
|
||||
fn taper_envelope(t: &ThreadInfo, radius: f64, ends: [End; 2], (d0, from): (f64, usize)) -> Result<Option<Body>> {
|
||||
let p = t.pitch;
|
||||
let k = *t.axis;
|
||||
let u = k.cross(if k.x.abs() < 0.9 { Vec3::new(1.0, 0.0, 0.0) } else { Vec3::new(0.0, 1.0, 0.0) });
|
||||
let u = u.normalized().ok_or_else(|| DocError::Invalid("thread axis".into()))?;
|
||||
let (at, into) = if from == 0 { (t.start, k) } else { (t.end, -k) };
|
||||
let far = 1 - from.min(1);
|
||||
let len = t.end - t.start + if ends.get(far) == Some(&End::Free) { p } else { 0.0 };
|
||||
let e = p;
|
||||
// In the plane: x radial, y along the axis into the material from the start.
|
||||
let plane = Plane { origin: t.axis_point + k * at, x: u, y: into };
|
||||
let pts = if t.internal {
|
||||
// The minor cone shrinks into the hole: what lies inside it, down to well inside the hole.
|
||||
let minor0 = t.form.groove(d0, p, true).crest_r;
|
||||
let rm = |y: f64| minor0 - y / 32.0;
|
||||
if !(rm(-e) > radius) {
|
||||
return Ok(None);
|
||||
}
|
||||
let r_in = 0.5 * radius.min(rm(len));
|
||||
if !(r_in > 0.0) {
|
||||
return Err(DocError::Invalid("the hole is too small for this taper thread".into()));
|
||||
}
|
||||
vec![Vec2::new(r_in, -e), Vec2::new(rm(-e), -e), Vec2::new(rm(len), len), Vec2::new(r_in, len)]
|
||||
} else {
|
||||
// The major cone grows into the shaft: what lies outside it, out past the surface.
|
||||
let rc = |y: f64| d0 / 2.0 + y / 32.0;
|
||||
let r_out = radius + 0.1 * p;
|
||||
if !(rc(-e) < r_out) {
|
||||
return Ok(None);
|
||||
}
|
||||
let hit = (r_out - d0 / 2.0) * 32.0;
|
||||
if hit < len {
|
||||
vec![Vec2::new(rc(-e), -e), Vec2::new(r_out, hit), Vec2::new(r_out, -e)]
|
||||
} else {
|
||||
vec![Vec2::new(rc(-e), -e), Vec2::new(rc(len), len), Vec2::new(r_out, len), Vec2::new(r_out, -e)]
|
||||
}
|
||||
};
|
||||
let region = Region2 { outer: solidcraft_geom::Loop2::polygon(&pts), holes: Vec::new() };
|
||||
let mut ring = kernel::revolve(&plane, &[region], Vec2::new(0.0, 0.0), Vec2::new(0.0, 1.0), std::f64::consts::TAU)?;
|
||||
Ok(ring.pop())
|
||||
}
|
||||
|
||||
/// Evaluate a thread feature: a cosmetic thread is listed in the model's threads, a modelled
|
||||
/// one cut into its body.
|
||||
pub(super) fn eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st: &mut ModelState) -> Result<()> {
|
||||
let FeatureKind::Thread {
|
||||
face,
|
||||
designation,
|
||||
length,
|
||||
modeled,
|
||||
from,
|
||||
up_to,
|
||||
form,
|
||||
hand,
|
||||
starts,
|
||||
ease_out,
|
||||
tip,
|
||||
chamfer_angle,
|
||||
chamfer_depth,
|
||||
steep_flank,
|
||||
} = &f.kind
|
||||
else {
|
||||
return Err(DocError::Invalid("not a thread".into()));
|
||||
};
|
||||
if *tip != ThreadTip::None && !*modeled {
|
||||
return Err(DocError::Invalid("a chamfered tip is cut only on a modelled thread".into()));
|
||||
}
|
||||
let extent = match (length, up_to) {
|
||||
(Some(e), _) => Extent::Length(val(vals, e, Kind::Length)?),
|
||||
(None, Some(crate::ThreadLimit::Face(q))) => Extent::UpToPoint(q.0),
|
||||
(None, Some(crate::ThreadLimit::Plane(pl))) => Extent::UpTo(doc.resolve_plane_in(vals, pl, 0, Some(st))?),
|
||||
(None, None) => Extent::Full,
|
||||
};
|
||||
// A form left at its default is the one the designation names (`Tr20x4`).
|
||||
let form = (*form != ThreadForm::Metric).then_some(*form);
|
||||
let ask = ThreadAsk {
|
||||
form,
|
||||
designation: designation.as_deref(),
|
||||
hand: *hand,
|
||||
starts: *starts,
|
||||
steep: *steep_flank,
|
||||
extent,
|
||||
from: from.map(|q| q.0),
|
||||
modeled: *modeled,
|
||||
ease_out: (0.0, 0.0),
|
||||
tapped: false,
|
||||
};
|
||||
if ease_out.is_some() && !*modeled {
|
||||
return Err(DocError::Invalid("a runout is cut only on a modelled thread".into()));
|
||||
}
|
||||
let ask = ThreadAsk { ease_out: runout(vals, ease_out)?, ..ask };
|
||||
let on = thread_on(st, f.id, face.0, &ask)?;
|
||||
if !*modeled {
|
||||
st.threads.push(on.info);
|
||||
return Ok(());
|
||||
}
|
||||
let angle = chamfer_angle.as_deref().map(|e| val(vals, e, Kind::Angle)).transpose()?;
|
||||
let depth = chamfer_depth.as_deref().map(|e| val(vals, e, Kind::Length)).transpose()?;
|
||||
cut(st, on, (*tip, angle, depth))
|
||||
}
|
||||
|
||||
/// A runout as (mm, pitches): ISO 3508's standard 2.5 P and short 1.25 P, or a length.
|
||||
pub(super) fn runout(vals: &BTreeMap<String, Value>, e: &Option<crate::ThreadRunout>) -> Result<(f64, f64)> {
|
||||
Ok(match e {
|
||||
None => (0.0, 0.0),
|
||||
Some(crate::ThreadRunout::Standard) => (0.0, 2.5),
|
||||
Some(crate::ThreadRunout::Short) => (0.0, 1.25),
|
||||
Some(crate::ThreadRunout::Length { length }) => (val(vals, length, Kind::Length)?, 0.0),
|
||||
})
|
||||
}
|
||||
|
||||
/// Cut a modelled thread resolved on its face into its body: the grooves (SMLib's exact screw
|
||||
/// sweeps, a taper's crests by its envelope) by one B-rep boolean, then the chamfered tip
|
||||
/// (`tip`, with its angle and depth when given).
|
||||
pub(super) fn cut(st: &mut ModelState, on: ThreadOn, (tip, angle, depth): (ThreadTip, Option<f64>, Option<f64>)) -> Result<()> {
|
||||
let ThreadOn { info: t, cyl, ends, steep, ease, from: from_side } = on;
|
||||
let i = st.bodies.iter().position(|b| b.name == t.body).ok_or_else(|| DocError::Invalid("threaded body".into()))?;
|
||||
let Some(mb) = st.bodies.get(i).cloned() else { return Err(DocError::Invalid("body".into())) };
|
||||
let (input, radius) = (mb.traced(), cyl.radius);
|
||||
// The exact grooves: SMLib's screw sweeps, cut by one B-rep boolean.
|
||||
// A taper thread starts at its free end at the major diameter its standard puts there: an
|
||||
// internal one's gauge (reference) plane at its face, an external one's that far in.
|
||||
let taper = match t.form.is_taper() {
|
||||
true => {
|
||||
let z = t.form.pipe_size(t.major_diameter).ok_or_else(|| DocError::Invalid(format!("no pipe size {}", t.designation)))?;
|
||||
Some((if t.internal { t.major_diameter } else { t.major_diameter - z.gauge / 16.0 }, from_side))
|
||||
}
|
||||
false => None,
|
||||
};
|
||||
let mut grooves = Vec::new();
|
||||
for g in thread_grooves(&t, radius, (cyl.start, cyl.end), ends, steep, ease, taper)? {
|
||||
let section = Region2 { outer: solidcraft_geom::Loop2::polygon(&g.section), holes: Vec::new() };
|
||||
grooves.push(kernel::sweep_helix_radial(&g.plane, §ion, g.base, g.axis, g.pitch, g.turns, &g.law)?);
|
||||
}
|
||||
if let Some(taper) = taper
|
||||
&& let Some(envelope) = taper_envelope(&t, radius, ends, taper)?
|
||||
{
|
||||
grooves.push(envelope);
|
||||
}
|
||||
let tool = match grooves.as_slice() {
|
||||
[one] => one.clone(),
|
||||
many => kernel::union_all(&many.iter().collect::<Vec<_>>())?,
|
||||
};
|
||||
let (nb, groove_faces) = (kernel::boolean(&input.body, &tool, BoolOp::Cut)?, tool.face_count());
|
||||
let nb = nb.ok_or_else(|| DocError::Invalid("the thread removed everything".into()))?;
|
||||
// The groove's faces have no role to name them by.
|
||||
let groove = crate::lineage::Lineage::unnamed_faces(groove_faces);
|
||||
let lineage = input.lineage.and_then(|l| crate::lineage::Lineage::step(&nb, &[&l, &groove]));
|
||||
let mut nb = Traced { body: nb, lineage };
|
||||
if tip != ThreadTip::None {
|
||||
for cone in tip_cones(&t, radius, ends, (tip, from_side), angle, depth, taper)? {
|
||||
let faces = cone.face_count();
|
||||
let cut = kernel::boolean(&nb.body, &cone, BoolOp::Cut)?.ok_or_else(|| DocError::Invalid("the chamfer removed everything".into()))?;
|
||||
let unnamed = crate::lineage::Lineage::unnamed_faces(faces);
|
||||
let lineage = nb.lineage.and_then(|l| crate::lineage::Lineage::step(&cut, &[&l, &unnamed]));
|
||||
nb = Traced { body: cut, lineage };
|
||||
}
|
||||
}
|
||||
if let Some(slot) = st.bodies.get_mut(i) {
|
||||
*slot = ModelBody::traced_new(mb.name, nb, mb.feature);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The chamfered tip of an external thread (ISO 4753, chamfered end CH) at the free ends `tip`
|
||||
/// names (`Start`: the end of the axis `from`, which must be free; `Both`: each free end):
|
||||
/// the material outside a cone `angle` to the axis (default 45°) that reaches the diameter
|
||||
/// d_f = d - 2 `depth` at the face's end, as a ring to cut after the groove, so the last teeth
|
||||
/// taper into it. ISO 4753 bounds d_f by the external thread's minor diameter; the default is
|
||||
/// that bound, d3 = d - 1.226869 P (ISO 724, the rounded root of the design profile). It lies
|
||||
/// below the modelled basic root d1, so the cone crosses the root cylinder and leaves a whole
|
||||
/// ring on the end; one ending on the root itself would pinch to nothing wherever the groove
|
||||
/// reaches the end. ISO 4753 is for ISO metric threads: other forms give `depth`. A taper
|
||||
/// thread's `taper` (its major diameter where it starts, the end it starts from) puts each end's
|
||||
/// major diameter where the taper has it there, and `depth` is measured from that.
|
||||
fn tip_cones(
|
||||
t: &ThreadInfo,
|
||||
radius: f64,
|
||||
ends: [End; 2],
|
||||
(tip, from): (ThreadTip, usize),
|
||||
angle: Option<f64>,
|
||||
depth: Option<f64>,
|
||||
taper: Option<(f64, usize)>,
|
||||
) -> Result<Vec<Body>> {
|
||||
if t.internal {
|
||||
return Err(DocError::Invalid("a chamfered tip is for an external thread (ISO 4753); countersink the hole's entry instead".into()));
|
||||
}
|
||||
let g = t.form.groove(t.major_diameter, t.pitch, false);
|
||||
let a = angle.unwrap_or(std::f64::consts::FRAC_PI_4);
|
||||
if !(a > 1f64.to_radians() && a < 89f64.to_radians()) {
|
||||
return Err(DocError::Invalid("the chamfer's angle to the axis must be between 1° and 89°".into()));
|
||||
}
|
||||
// The major diameter at each end of the axis: a taper's grows 1 in 16 from where it starts.
|
||||
let major = |side: usize| match taper {
|
||||
Some((d0, start)) if side == start => d0,
|
||||
Some((d0, _)) => d0 + (t.end - t.start) / 16.0,
|
||||
None => t.major_diameter,
|
||||
};
|
||||
let depth = match (depth, t.form) {
|
||||
(Some(d), _) => d,
|
||||
// ISO 724: d3 = d1 - H/6 = d - 2 (17/24) H.
|
||||
(None, ThreadForm::Metric) => 17.0 / 24.0 * t.pitch * 3f64.sqrt() / 2.0,
|
||||
(None, form) => {
|
||||
return Err(DocError::Invalid(format!(
|
||||
"ISO 4753's chamfered end is for ISO metric threads: give a {} thread its chamfer depth (its root is {} mm deep)",
|
||||
form.name(),
|
||||
show_mm(t.major_diameter / 2.0 - g.root_r)
|
||||
)));
|
||||
}
|
||||
};
|
||||
let smallest = major(0).min(major(1)) / 2.0;
|
||||
if !(depth > 0.0 && smallest - depth > 1e-3) {
|
||||
return Err(DocError::Invalid(format!("the chamfer's depth must be between 0 and {} mm", show_mm(smallest))));
|
||||
}
|
||||
let k = *t.axis;
|
||||
let u = k.cross(if k.x.abs() < 0.9 { Vec3::new(1.0, 0.0, 0.0) } else { Vec3::new(0.0, 1.0, 0.0) });
|
||||
let u = u.normalized().ok_or_else(|| DocError::Invalid("thread axis".into()))?;
|
||||
let slope = a.tan();
|
||||
// Past the end only the radii the groove's overrun already reaches (r >= d_f / 2), so
|
||||
// nothing past a free end beyond the thread is touched.
|
||||
let e = 0.5 * t.pitch;
|
||||
let mut out = Vec::new();
|
||||
let free = |side: usize| ends.get(side) == Some(&End::Free);
|
||||
if tip == ThreadTip::Start && !free(from) {
|
||||
return Err(DocError::Invalid("the end the thread starts from is not a free end (material goes on past it, or the thread stops inside the face): chamfer it with `tip: both`, or start the thread from its free end".into()));
|
||||
}
|
||||
for (side, at, out_dir) in [(0usize, t.start, -k), (1, t.end, k)] {
|
||||
if !free(side) || (tip == ThreadTip::Start && side != from) {
|
||||
continue;
|
||||
}
|
||||
let r_f = major(side) / 2.0 - depth;
|
||||
let r_out = radius.max(major(side) / 2.0) + 0.1 * t.pitch;
|
||||
// In the plane: x radial, y outward along the axis from the end.
|
||||
let plane = Plane { origin: t.axis_point + k * at, x: u, y: out_dir };
|
||||
let pts = [Vec2::new(r_f, 0.0), Vec2::new(r_f, e), Vec2::new(r_out, e), Vec2::new(r_out, -(r_out - r_f) / slope)];
|
||||
let region = Region2 { outer: solidcraft_geom::Loop2::polygon(&pts), holes: Vec::new() };
|
||||
let mut ring = kernel::revolve(&plane, &[region], Vec2::new(0.0, 0.0), Vec2::new(0.0, 1.0), std::f64::consts::TAU)?;
|
||||
out.push(ring.pop().ok_or_else(|| DocError::Invalid("chamfer".into()))?);
|
||||
}
|
||||
if out.is_empty() {
|
||||
return Err(DocError::Invalid("a chamfered tip needs a free end of the thread (one with no material past it)".into()));
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
//! Extrudes from a whole face whose outline is no lines and arcs (#2040), checked through the
|
||||
//! tool `whole_face_tool` picks.
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
use serde_json::json;
|
||||
use solidcraft_geom::{Plane, Vec2, Vec3};
|
||||
use solidcraft_kernel as kernel;
|
||||
use solidcraft_sketch::{CurveKind, Sketch};
|
||||
|
||||
use crate::*;
|
||||
|
||||
thread_local! {
|
||||
static SLABS: Cell<usize> = const { Cell::new(0) };
|
||||
}
|
||||
|
||||
/// A cut took a slab over the whole body as its tool, on this thread.
|
||||
pub(super) fn slab_taken() {
|
||||
SLABS.with(|n| n.set(n.get() + 1));
|
||||
}
|
||||
|
||||
/// A closed spline through six points round the origin, on the plane at height `z`.
|
||||
fn spline_sketch(z: f64) -> FeatureKind {
|
||||
let mut sk = Sketch::new();
|
||||
let fit = [(8.0, 0.0), (4.0, 6.0), (-3.0, 7.0), (-7.0, 1.0), (-4.0, -6.0), (3.0, -5.0)];
|
||||
let mut pts: Vec<usize> = fit.iter().map(|&(x, y)| sk.add_point(Vec2::new(x, y), None).unwrap()).collect();
|
||||
pts.push(pts[0]);
|
||||
sk.add_curve(CurveKind::Spline { pts, control: false, degree: 3, periodic: true }, None).unwrap();
|
||||
let plane = Plane { origin: Vec3::new(0.0, 0.0, z), ..Plane::XY };
|
||||
FeatureKind::Sketch { plane: PlaneRef::Custom { plane }, sketch: sk }
|
||||
}
|
||||
|
||||
fn extrude(doc: &mut Document, sketch: u64, extent: serde_json::Value, operation: Operation) {
|
||||
let kind = FeatureKind::Extrude {
|
||||
thin: None,
|
||||
sketch: SketchId(sketch),
|
||||
profiles: ProfileSel::All,
|
||||
extent: serde_json::from_value(extent).unwrap(),
|
||||
operation,
|
||||
targets: vec![],
|
||||
};
|
||||
doc.add_feature(kind, None).unwrap();
|
||||
}
|
||||
|
||||
fn volume(model: &Model) -> f64 {
|
||||
let state = model.state();
|
||||
assert!(model.results.iter().all(|r| r.error.is_none()), "{:?}", model.results.iter().find(|r| r.error.is_some()));
|
||||
assert_eq!(state.bodies.len(), 1);
|
||||
assert!(state.bodies[0].body.validity().is_empty(), "{:?}", state.bodies[0].body.validity());
|
||||
kernel::measure(&state.bodies[0].body).unwrap().volume
|
||||
}
|
||||
|
||||
/// A prism 10 tall on a closed spline, standing on a 40 x 40 x 5 plate, cut 3 into its whole
|
||||
/// top face. The walls only touch the prism, so the cut takes a slab over the whole body from 3
|
||||
/// below the face up: it must take the face's area times 3 and leave the plate alone.
|
||||
#[test]
|
||||
fn slab_cut_into_a_spline_prism() {
|
||||
let mut doc = Document::new("spline prism");
|
||||
let plate = FeatureKind::Box {
|
||||
corner: point_expr(Vec3::new(-20.0, -20.0, 0.0)),
|
||||
length: "40 mm".into(),
|
||||
width: "40 mm".into(),
|
||||
height: "5 mm".into(),
|
||||
operation: Operation::NewBody,
|
||||
};
|
||||
doc.add_feature(plate, None).unwrap();
|
||||
let base = doc.add_feature(spline_sketch(5.0), None).unwrap();
|
||||
extrude(&mut doc, base, json!({"distance": "10 mm"}), Operation::Join);
|
||||
let mut model = Model::new();
|
||||
model.evaluate(&doc);
|
||||
let joined = volume(&model);
|
||||
let area = (joined - 40.0 * 40.0 * 5.0) / 10.0;
|
||||
assert!(area > 100.0, "the prism's area {area}");
|
||||
|
||||
let top = doc.add_feature(spline_sketch(15.0), None).unwrap();
|
||||
extrude(&mut doc, top, json!({"distance": "3 mm", "direction": "negative"}), Operation::Cut);
|
||||
SLABS.with(|n| n.set(0));
|
||||
model.evaluate(&doc);
|
||||
let cut = volume(&model);
|
||||
assert_eq!(SLABS.with(Cell::get), 1, "the cut took the slab");
|
||||
assert!((joined - cut - area * 3.0).abs() < 1e-3 * area, "took {}, expected {}", joined - cut, area * 3.0);
|
||||
let bounds = model.state().bodies[0].mesh().bounds();
|
||||
assert!((bounds.max.z - 12.0).abs() < 1e-6, "top at {}", bounds.max.z);
|
||||
assert!(bounds.min.dist(Vec3::new(-20.0, -20.0, 0.0)) < 1e-6, "plate from {:?}", bounds.min);
|
||||
}
|
||||
+69
-1
@@ -304,6 +304,28 @@ pub fn value_in_unit(v: Value, unit: &str) -> f64 {
|
||||
}
|
||||
}
|
||||
|
||||
/// The number a reply reports for a value: millimetres for a length, degrees for an angle,
|
||||
/// as-is for a unit-less or compound value. One meaning of `value` in every reply (#10747).
|
||||
pub fn reported_value(v: Value) -> f64 {
|
||||
match v.kind() {
|
||||
Some(Kind::Angle) => v.v.to_degrees(),
|
||||
_ => v.v,
|
||||
}
|
||||
}
|
||||
|
||||
/// The number a person would read for `v`: the shortest decimal that reads back after
|
||||
/// rounding off the last few bits (12 significant digits), so `30 deg` stored as
|
||||
/// 29.999999999999996 reports 30 and `0.1 + 0.2` reports 0.3. Only what is *reported*
|
||||
/// is rounded; stored values and evaluation keep full precision (#10747).
|
||||
pub fn round_reported(v: f64) -> f64 {
|
||||
if !v.is_finite() {
|
||||
return v;
|
||||
}
|
||||
// `:.11e` keeps 12 significant digits; parsing back gives the f64 nearest that decimal,
|
||||
// which serializes as the shortest form that reads back to it.
|
||||
format!("{v:.11e}").parse().unwrap_or(v)
|
||||
}
|
||||
|
||||
fn unit(name: &str) -> Option<Value> {
|
||||
UNITS.iter().find(|(n, _, _)| *n == name).map(|(_, k, s)| match k {
|
||||
Kind::Angle => Value::angle(*s),
|
||||
@@ -950,7 +972,11 @@ fn call(f: &str, args: &[Value], d: Defaults) -> Result<Value, DocError> {
|
||||
let a = one()?;
|
||||
unit_fn(a.v.abs(), a)
|
||||
}
|
||||
"sign" => Value::num(one()?.v.signum()),
|
||||
"sign" => {
|
||||
let x = one()?.v;
|
||||
// `f64::signum` gives 1 for +0 and -1 for -0; the sign of zero is 0.
|
||||
Value::num(if x == 0.0 { 0.0 } else { x.signum() })
|
||||
}
|
||||
"floor" | "ceil" | "round" | "trunc" => {
|
||||
let a = one()?;
|
||||
// Round in the value's own default unit (round(2.4 in) = 2 in for an inch parameter).
|
||||
@@ -1408,6 +1434,16 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// `sign(0)` is 0, not 1 (Rust's `signum` takes the sign bit, so it calls zero positive).
|
||||
#[test]
|
||||
fn sign_of_zero_is_zero() {
|
||||
for (e, want) in
|
||||
[("sign(0)", 0.0), ("sign(-0)", 0.0), ("sign(0 mm)", 0.0), ("sign(5)", 1.0), ("sign(-2 mm)", -1.0), ("sign(width - 40)", 0.0)]
|
||||
{
|
||||
assert_eq!(ev(e, Kind::Unitless).unwrap(), want, "{e}");
|
||||
}
|
||||
}
|
||||
|
||||
/// A count from a length (`(L - 20) / 210`, the 210 read as unit-less) names the fix (#5901).
|
||||
#[test]
|
||||
fn a_length_where_a_count_is_expected_says_to_divide_by_a_length() {
|
||||
@@ -1637,4 +1673,36 @@ mod tests {
|
||||
defs[2].expr = "bad +".into();
|
||||
assert!(eval_params(&defs, 1.0).1.contains_key("c"));
|
||||
}
|
||||
|
||||
/// #10747: reported numbers are the shortest decimals that read back after rounding off
|
||||
/// the last few bits (12 significant digits); stored values keep full precision.
|
||||
#[test]
|
||||
fn round_reported_reads_as_a_person_would() {
|
||||
for (noisy, read) in [
|
||||
(30f64.to_radians().to_degrees(), 30.0),
|
||||
(0.1 + 0.2, 0.3),
|
||||
(3.0 * 25.4, 76.2),
|
||||
(1.2 * 25.4, 30.48),
|
||||
(29.999999999999996, 30.0),
|
||||
(0.30000000000000004, 0.3),
|
||||
(-0.1 - 0.2, -0.3),
|
||||
] {
|
||||
assert_eq!(round_reported(noisy), read, "{noisy}");
|
||||
}
|
||||
// A number with real digits of its own keeps them up to 12 significant digits.
|
||||
assert_eq!(round_reported(1.234567890123), 1.23456789012);
|
||||
assert_eq!(round_reported(1.0000000000001), 1.0);
|
||||
// Zero and non-finite values pass through.
|
||||
assert_eq!(round_reported(0.0), 0.0);
|
||||
assert!(round_reported(f64::INFINITY).is_infinite());
|
||||
assert!(round_reported(f64::NAN).is_nan());
|
||||
}
|
||||
|
||||
/// #10747: `value` means millimetres and degrees in every reply.
|
||||
#[test]
|
||||
fn reported_value_is_mm_and_degrees() {
|
||||
assert_eq!(reported_value(Value::length(50.8)), 50.8);
|
||||
assert_eq!(round_reported(reported_value(Value::angle(30f64.to_radians()))), 30.0);
|
||||
assert_eq!(reported_value(Value::num(4.0)), 4.0);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user