41 Commits

Author SHA1 Message Date
utkabobr 12b370ce30 Change Google Play button padding in readme 2025-04-14 19:25:59 +03:00
utkabobr dd7a6ddf1d Quick fix 2025-04-08 03:02:51 +03:00
utkabobr 9b32fe68d7 Increase app boot polling rate a bit 2025-04-06 21:47:43 +03:00
utkabobr 33487afdc9 Fix import 2025-04-06 21:35:31 +03:00
utkabobr bc1007aa59 Use instanced shaders manager 2025-04-06 21:24:47 +03:00
utkabobr 4c4469b1fd Allow cloud import from setup screen 2025-04-06 21:16:00 +03:00
utkabobr e4320be0ce Hide cloud if levels is empty 2025-04-06 20:29:57 +03:00
utkabobr 0933adf1b9 Disallow long click to move in gcode preview 2025-04-06 20:18:17 +03:00
utkabobr 70f4d08a8c Cloud sync made right 2025-04-06 20:02:28 +03:00
utkabobr a27a8c1d5d Cloud features: part 1 2025-04-06 05:38:54 +03:00
utkabobr abf53f1c43 Add google play link to readme 2025-04-05 19:13:02 +03:00
utkabobr 5f13961d05 Long click to move; Boosty page on click before redirect 2025-04-04 19:18:57 +03:00
utkabobr 264e742d3a Settings delta preview 2025-04-03 20:08:33 +03:00
utkabobr 462f0a4c55 I forgor 2025-04-03 19:29:58 +03:00
utkabobr 6bb8926fb7 Friendly error page for web panel 2025-04-03 19:24:41 +03:00
utkabobr e1fe683154 Schedule truetime as non-critical task 2025-04-02 17:10:02 +03:00
utkabobr 8529d94899 Export to 3mf 2025-04-02 17:00:59 +03:00
utkabobr df09f8ef39 Convert variables in converted profiles. Small snackbar changes 2025-04-02 14:14:35 +03:00
utkabobr 8b200e689c Implement additional alternative camera control method 2025-04-02 03:43:49 +03:00
utkabobr 838cedbf1a Use largeHeap, could help on some devices 2025-04-02 02:09:20 +03:00
utkabobr e52a1ad949 Fix scale in mm pref 2025-04-02 01:41:24 +03:00
utkabobr 2c1d45153b Allow more zoom in camera 2025-04-02 01:40:52 +03:00
utkabobr 6ede5931e8 Add tooltips on long click (For booleans) 2025-04-02 01:40:34 +03:00
utkabobr 1976ac4899 New snackbars 2025-04-02 01:38:45 +03:00
utkabobr 58b24d5137 Better truetime init 2025-04-02 01:35:16 +03:00
utkabobr 662018834e Optimize boot time 2025-04-01 23:06:08 +03:00
utkabobr 6092f123b5 Scale in MM 2025-03-06 04:17:16 +03:00
utkabobr 0f8985377a A bit better en translation 2025-03-02 22:12:34 +03:00
utkabobr 12b682b62b Bump version 2025-03-02 22:05:46 +03:00
utkabobr 7f9b1b908a Auto-orientation 2025-03-02 22:05:22 +03:00
utkabobr 540f8f6c7c Add qidi profiles 2025-03-02 20:48:35 +03:00
utkabobr eb0e0c189b New flatten icon, migrate libslic3r_version to use gradle variables 2025-03-02 20:17:33 +03:00
YTKAB0BP ecc9fc6f48 WIP: Port BBL auto-orient 2025-02-26 18:45:17 +03:00
YTKAB0BP 1eb405b07f WIP: Flatten to surface feature; Fix rotating multiple axis 2025-02-26 02:15:27 +03:00
YTKAB0BP 17b6b6c8ec Display web pannel if configured 2025-02-25 22:02:36 +03:00
YTKAB0BP 55610b408a Fix anybuic profile, disable ac_filament_type 2025-02-25 20:40:38 +03:00
YTKAB0BP c86f306caf Fix bed_shape convertion 2024-11-26 19:17:08 +03:00
YTKAB0BP 368a5cca00 Fix legacy enums support 2024-11-22 20:29:19 +03:00
YTKAB0BP 4a6a290f6d Support vendor dumps repo 2024-11-21 21:37:43 +03:00
YTKAB0BP c138b0b259 Disable arc_fitting 2024-11-21 21:22:57 +03:00
YTKAB0BP 14dc402945 Dumped anycubic's slicer profiles 2024-11-21 21:16:20 +03:00
150 changed files with 20517 additions and 569 deletions
+93
View File
@@ -0,0 +1,93 @@
<svg width="270" height="80" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#a)">
<mask id="b" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
</mask>
<g mask="url(#b)">
<path d="M260 80H10A10 10 0 0 1 0 70V10A10 10 0 0 1 10 0h250a10 10 0 0 1 10 10v60a10 10 0 0 1-10 10Z" fill="#000"/>
</g>
<mask id="c" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
</mask>
<g mask="url(#c)">
<path d="M94.8 20.5c0 1.7-.5 3-1.5 4a5.8 5.8 0 0 1-4.4 1.8 6 6 0 0 1-4.4-1.8 6 6 0 0 1-1.8-4.5 6 6 0 0 1 1.8-4.5 6 6 0 0 1 4.4-1.8 7 7 0 0 1 2.5.5 5 5 0 0 1 1.9 1.4l-1 1a4 4 0 0 0-3.4-1.4 4.6 4.6 0 0 0-4.7 4.8c0 1.4.5 2.6 1.5 3.5.9.9 2 1.3 3.2 1.3a5 5 0 0 0 3.4-1.3c.6-.6.9-1.4 1-2.5h-4.4v-1.4h5.9v.9Z" fill="#fff" stroke="#fff" stroke-width=".3" stroke-miterlimit="10"/>
</g>
<mask id="d" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
</mask>
<g mask="url(#d)">
<path d="M104 15.5h-5.4v3.8h5v1.4h-5v3.8h5.5V26h-7V14h7v1.5Z" fill="#fff" stroke="#fff" stroke-width=".3" stroke-miterlimit="10"/>
</g>
<mask id="e" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
</mask>
<g mask="url(#e)">
<path d="M110.6 26H109V15.5h-3.3V14h8.2v1.5h-3.3V26Z" fill="#fff" stroke="#fff" stroke-width=".3" stroke-miterlimit="10"/>
</g>
<mask id="f" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
</mask>
<g mask="url(#f)">
<path d="M119.9 14h1.5v12H120V14Z" fill="#fff" stroke="#fff" stroke-width=".3" stroke-miterlimit="10"/>
</g>
<mask id="g" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
</mask>
<g mask="url(#g)">
<path d="M128.3 26h-1.6V15.5h-3.3V14h8.2v1.5h-3.3V26Z" fill="#fff" stroke="#fff" stroke-width=".3" stroke-miterlimit="10"/>
</g>
<mask id="h" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
</mask>
<g mask="url(#h)">
<path d="M139.6 23.4a4 4 0 0 0 3.2 1.4 4.7 4.7 0 0 0 4.6-4.8c0-1.4-.4-2.5-1.3-3.4-1-1-2-1.4-3.3-1.4-1.3 0-2.4.5-3.2 1.4-1 .9-1.4 2-1.4 3.4s.5 2.5 1.4 3.4Zm7.6 1a5.9 5.9 0 0 1-4.4 1.9c-1.7 0-3.2-.6-4.4-1.9a6.1 6.1 0 0 1-1.7-4.4 6.1 6.1 0 0 1 6.1-6.3c1.8 0 3.2.6 4.4 1.9A6.1 6.1 0 0 1 149 20c0 1.8-.6 3.2-1.8 4.4Z" fill="#fff"/>
</g>
<mask id="i" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
</mask>
<g mask="url(#i)">
<path d="M139.6 23.4a4 4 0 0 0 3.2 1.4 4.7 4.7 0 0 0 4.6-4.8c0-1.4-.4-2.5-1.3-3.4-1-1-2-1.4-3.3-1.4-1.3 0-2.4.5-3.2 1.4-1 .9-1.4 2-1.4 3.4s.5 2.5 1.4 3.4Zm7.6 1a5.9 5.9 0 0 1-4.4 1.9c-1.7 0-3.2-.6-4.4-1.9a6.1 6.1 0 0 1-1.7-4.4 6.1 6.1 0 0 1 6.1-6.3c1.8 0 3.2.6 4.4 1.9A6.1 6.1 0 0 1 149 20c0 1.8-.6 3.2-1.8 4.4Z" stroke="#fff" stroke-width=".3" stroke-miterlimit="10"/>
</g>
<mask id="j" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
</mask>
<g mask="url(#j)">
<path d="M151.2 26V14h1.8l5.9 9.3V14h1.5v12h-1.6l-6.1-9.8V26h-1.5Z" fill="#fff" stroke="#fff" stroke-width=".3" stroke-miterlimit="10"/>
</g>
<mask id="k" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
</mask>
<g mask="url(#k)">
<path d="M213.9 60h3.7V35h-3.7v25Zm33.6-16-4.3 10.8h-.1L238.6 44h-4l6.7 15.2-3.8 8.4h3.9L251.6 44h-4.1Zm-21.2 13.2c-1.2 0-3-.6-3-2.2 0-1.9 2.2-2.6 4-2.6 1.7 0 2.5.3 3.5.8-.3 2.3-2.3 4-4.5 4Zm.5-13.7c-2.7 0-5.5 1.1-6.7 3.8l3.3 1.4c.7-1.4 2-1.9 3.4-1.9 2 0 4 1.2 4 3.3v.2c-.7-.4-2.2-1-4-1-3.5 0-7.1 2-7.1 5.7 0 3.3 2.9 5.5 6.2 5.5 2.5 0 3.9-1.2 4.7-2.5h.2v2h3.6v-9.6c0-4.5-3.3-7-7.6-7Zm-23 3.5h-5.4v-8.5h5.3c2.8 0 4.4 2.3 4.4 4.3 0 1.9-1.6 4.2-4.4 4.2Zm-.2-12h-9v25h3.8v-9.5h5.2c4.1 0 8.2-3 8.2-7.7 0-4.8-4-7.8-8.2-7.8Zm-48.8 22.2c-2.5 0-4.7-2.2-4.7-5.2s2.2-5.1 4.8-5.1c2.5 0 4.5 2.1 4.5 5.1 0 3-2 5.2-4.6 5.2Zm4.3-11.8c-1-1-2.5-1.9-4.6-1.9a8.5 8.5 0 0 0-8.1 8.5c0 4.8 3.9 8.5 8.1 8.5 2 0 3.7-.9 4.5-2h.1v1.3c0 3.3-1.7 5-4.5 5a4.7 4.7 0 0 1-4.3-3l-3.2 1.3a8 8 0 0 0 7.5 5c4.4 0 8-2.5 8-8.8V44h-3.5v1.4Zm6.2 14.6h3.7V35h-3.7v25Zm9.2-8.2c0-3.3 2.6-5 4.4-5 1.5 0 2.8.7 3.2 1.8l-7.6 3.2Zm11.6-2.9c-.7-1.9-2.9-5.4-7.3-5.4s-8 3.5-8 8.5c0 4.8 3.6 8.5 8.4 8.5 4 0 6.2-2.4 7.1-3.8l-2.9-1.9a4.9 4.9 0 0 1-4.2 2.4c-1.9 0-3.2-1-4-2.6l11.3-4.7-.4-1Zm-90.6-2.8v3.6h8.6c-.2 2-1 3.5-2 4.6a8.8 8.8 0 0 1-6.6 2.6 9.5 9.5 0 0 1-9.5-9.6 9.5 9.5 0 0 1 16-7l2.5-2.6A13.4 13.4 0 0 0 82 47.3c0 7.3 6.1 13.2 13.4 13.2 4 0 6.9-1.3 9.2-3.7a12 12 0 0 0 3.1-8.4c0-.9 0-1.6-.2-2.3h-12Zm22.1 11c-2.5 0-4.8-2-4.8-5 0-3.2 2.3-5.2 4.8-5.2 2.6 0 4.8 2 4.8 5.1 0 3-2.2 5.2-4.8 5.2Zm0-13.6a8.4 8.4 0 0 0-8.5 8.5c0 5 3.8 8.5 8.5 8.5 4.8 0 8.6-3.6 8.6-8.5 0-5-3.9-8.5-8.6-8.5Zm18.7 13.7c-2.6 0-4.8-2.2-4.8-5.2s2.2-5.1 4.8-5.1c2.5 0 4.8 2 4.8 5.1 0 3-2.3 5.2-4.8 5.2Zm0-13.7a8.4 8.4 0 0 0-8.6 8.5c0 5 3.9 8.5 8.6 8.5 4.7 0 8.5-3.6 8.5-8.5 0-5-3.8-8.5-8.5-8.5Z" fill="#fff"/>
</g>
<mask id="l" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
</mask>
<g mask="url(#l)">
<path d="M41.4 38.8 20.1 61.4a5.7 5.7 0 0 0 8.5 3.5l24-13.8-11.2-12.3Z" fill="#EA4335"/>
</g>
<mask id="m" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
</mask>
<g mask="url(#m)">
<path d="m63 35-10.4-6L41 39.4 52.7 51l10.2-6a5.8 5.8 0 0 0 .1-10Z" fill="#FBBC04"/>
</g>
<mask id="n" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
</mask>
<g mask="url(#n)">
<path d="M20.1 18.6 20 20v40l.2 1.4 22-22-22-20.8Z" fill="#4285F4"/>
</g>
<mask id="o" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
</mask>
<g mask="url(#o)">
<path d="m41.6 40 11-11-24-13.9a5.8 5.8 0 0 0-8.5 3.4L41.6 40Z" fill="#34A853"/>
</g>
</g>
<defs>
<clipPath id="a">
<path fill="#fff" transform="matrix(1 0 0 -1 0 80)" d="M0 0h270v80H0z"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 6.9 KiB

+3
View File
@@ -30,6 +30,9 @@ app/src/main/jni/occt_wrapper/occtwrapper_export.h
app/release/
.idea/
.project
.classpath
.settings
app/src/main/jniImports/boost/
app/src/main/jniImports/oneTBB/
+3
View File
@@ -1,3 +1,6 @@
[submodule "EventBus"]
path = EventBus
url = https://github.com/utkabobr/EventBus
[submodule "SAPIL"]
path = SAPIL
url = https://github.com/utkabobr/SAPIL
+64
View File
@@ -0,0 +1,64 @@
min_slic3r_version = 1.4.0
1.4.34 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.33 add filament_type_sub parameters
min_slic3r_version = 1.4.0
1.4.32 add filament_type_sub parameters
min_slic3r_version = 1.4.0
1.4.31 add filament_type_sub parameters
min_slic3r_version = 1.4.0
1.4.30 update Kobra 3 pro parameters, add Anycubic PLA High Speed filaments
min_slic3r_version = 1.4.0
1.4.29 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.28 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.27 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.26 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.25 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.23 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.22 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.21 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.20 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.19 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.18 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.17 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.16 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.15 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.14 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.13 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.12 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.11 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.10 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.9 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.8 update Kobra 3 pro parameters
min_slic3r_version = 1.4.0
1.4.7 update Kobra 2 serials Optimize printing parameters
min_slic3r_version = 1.3.0
1.4.5 update Kobra 2 serials
min_slic3r_version = 1.1.5
1.4.3 update Kobra 2 serials
min_slic3r_version = 1.1.1
1.4.2 Add Printer:Kobra 2 Plus; Kobra 2 Max
min_slic3r_version = 1.1.0
1.4.1 Add Printer:Kobra 2 P; Kobra 2 Neo
min_slic3r_version = 1.0.1
1.4.0 Initial Version
File diff suppressed because it is too large Load Diff
Binary file not shown.
+59
View File
@@ -0,0 +1,59 @@
<svg width="220" height="220" viewBox="0 0 220 220" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1968_5896)">
<g clip-path="url(#clip1_1968_5896)">
<path d="M20.6667 -4.93579C20.6667 4.75448 20.6667 187.768 20.6667 278.064" stroke="#9B9B9B" stroke-width="0.166667"/>
<path d="M50.3333 -4.93579C50.3333 4.75448 50.3333 187.768 50.3333 278.064" stroke="#9B9B9B" stroke-width="0.5"/>
<path d="M80 -4.93579C80 4.75448 80 187.768 80 278.064" stroke="#9B9B9B" stroke-width="0.166667"/>
<path d="M109.667 -4.93579C109.667 4.75448 109.667 187.768 109.667 278.064" stroke="#9B9B9B" stroke-width="0.5"/>
<path d="M139.333 -4.93579C139.333 4.75448 139.333 187.768 139.333 278.064" stroke="#9B9B9B" stroke-width="0.166667"/>
<path d="M169 -4.93579C169 1.9467 169 131.932 169 196.064" stroke="#9B9B9B" stroke-width="0.666667"/>
<path d="M198.667 -4.93579C198.667 1.9467 198.667 131.932 198.667 196.064" stroke="#9B9B9B" stroke-width="0.166667"/>
</g>
</g>
<g clip-path="url(#clip2_1968_5896)">
<g clip-path="url(#clip3_1968_5896)">
<path d="M0 21.3976C7.87549 21.3976 156.615 21.3976 230 21.3976" stroke="#9B9B9B" stroke-width="0.166667"/>
<path d="M0 51.0642C7.87549 51.0642 156.615 51.0642 230 51.0642" stroke="#9B9B9B" stroke-width="0.5"/>
<path d="M0 80.7308C7.87549 80.7308 156.615 80.7308 230 80.7308" stroke="#9B9B9B" stroke-width="0.166667"/>
<path d="M0 110.398C7.87549 110.398 156.615 110.398 230 110.398" stroke="#9B9B9B" stroke-width="0.5"/>
<path d="M0 140.064C7.87549 140.064 156.615 140.064 230 140.064" stroke="#9B9B9B" stroke-width="0.166667"/>
<path d="M0 169.731C7.87549 169.731 156.615 169.731 230 169.731" stroke="#9B9B9B" stroke-width="0.5"/>
<path d="M144 199.398C139.069 199.398 45.9455 199.398 6.73532e-06 199.398" stroke="#9B9B9B" stroke-width="0.166667"/>
</g>
</g>
<path opacity="0.9" fill-rule="evenodd" clip-rule="evenodd" d="M159.09 202.208L164.745 207.65L168.176 207.984L163.701 204.048L159.09 202.208Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M164.666 207.702L159.014 202.244L155.531 208.512L159.417 214.311L164.666 207.702Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M159.493 214.325L164.754 207.737L168.197 208.044L164.063 212.666L159.493 214.325Z" fill="white"/>
<path d="M184.734 205.437H183.223L184.761 207.613L185.53 206.569L184.734 205.437Z" fill="white"/>
<path d="M181.552 208.641L177.723 205.25V206.888L182.786 211.36V205.437H181.552V208.641Z" fill="white"/>
<path d="M187.243 205.437L185.197 208.235V211.176H186.436V208.637L188.772 205.437H187.243Z" fill="white"/>
<path d="M173.253 206.511L174.549 209.011H174.064L173.448 210.217H175.161L175.662 211.176H177.044L173.953 205.173L173.253 206.511Z" fill="white"/>
<path d="M177.723 211.176V211.178H178.957V208.883L177.723 207.799V211.176Z" fill="white"/>
<path d="M172.881 207.254L170.864 211.176H172.242L173.568 208.601L172.881 207.254Z" fill="white"/>
<path d="M204.778 208.834C204.976 208.784 205.162 208.697 205.326 208.577C205.548 208.415 205.727 208.201 205.847 207.954C205.967 207.706 206.024 207.433 206.013 207.159C206.02 206.892 205.962 206.628 205.843 206.389C205.724 206.15 205.548 205.945 205.33 205.791C205.157 205.662 204.961 205.568 204.752 205.514C204.468 205.454 204.178 205.428 203.887 205.437H203.577V206.655H204.02C204.212 206.64 204.404 206.671 204.581 206.744C204.654 206.786 204.713 206.847 204.752 206.921C204.792 206.996 204.809 207.079 204.803 207.163C204.809 207.26 204.786 207.357 204.736 207.441C204.686 207.524 204.612 207.59 204.523 207.631C204.347 207.693 204.16 207.719 203.974 207.708H203.577V208.914H204.02C204.275 208.918 204.53 208.891 204.778 208.834Z" fill="white"/>
<path d="M189.35 206.474C188.938 206.999 188.716 207.649 188.723 208.317C188.724 208.779 188.83 209.234 189.035 209.648C189.239 210.062 189.536 210.423 189.902 210.704C190.345 211.043 190.869 211.26 191.422 211.331V210.086C190.961 209.988 190.555 209.718 190.288 209.33C190.111 209.076 190.003 208.78 189.975 208.471C189.947 208.163 190 207.853 190.128 207.571C190.229 207.336 190.381 207.127 190.571 206.957C190.816 206.746 191.111 206.601 191.427 206.534V205.286C191.018 205.334 190.624 205.464 190.267 205.668C189.91 205.872 189.599 206.146 189.35 206.474Z" fill="white"/>
<path d="M193.202 207.234L194.168 206.465C193.93 206.142 193.629 205.873 193.281 205.674C192.922 205.472 192.525 205.343 192.116 205.295V206.542C192.552 206.629 192.939 206.875 193.202 207.234Z" fill="white"/>
<path d="M214.362 207.234L215.331 206.465C215.093 206.143 214.791 205.874 214.444 205.674C214.085 205.472 213.688 205.343 213.279 205.295V206.542C213.714 206.629 214.1 206.876 214.362 207.234Z" fill="white"/>
<path d="M204.811 209.297C204.82 209.337 204.825 209.378 204.825 209.419C204.835 209.525 204.808 209.631 204.749 209.72C204.69 209.809 204.603 209.874 204.501 209.906C204.332 209.953 204.158 209.973 203.983 209.966H203.586V211.172H203.991C204.257 211.177 204.522 211.153 204.783 211.101C204.978 211.056 205.162 210.974 205.326 210.859C205.549 210.702 205.73 210.492 205.853 210.248C205.981 209.999 206.048 209.723 206.048 209.443C206.05 209.186 205.999 208.932 205.897 208.696C205.62 209.022 205.234 209.235 204.811 209.297Z" fill="white"/>
<path d="M210.511 206.474C210.098 206.999 209.877 207.649 209.886 208.317C209.886 208.779 209.993 209.234 210.197 209.648C210.402 210.062 210.699 210.423 211.065 210.704C211.507 211.044 212.032 211.26 212.585 211.331V210.086C212.124 209.988 211.717 209.718 211.448 209.33C211.272 209.076 211.165 208.78 211.137 208.471C211.109 208.163 211.161 207.853 211.289 207.571C211.389 207.336 211.541 207.126 211.732 206.957C211.977 206.746 212.271 206.6 212.587 206.534V205.286C212.179 205.334 211.785 205.464 211.428 205.668C211.071 205.872 210.759 206.146 210.511 206.474Z" fill="white"/>
<path d="M192.109 210.088V211.336C192.516 211.292 192.91 211.165 193.267 210.963C193.623 210.761 193.934 210.488 194.181 210.161L193.206 209.406C192.937 209.761 192.546 210.004 192.109 210.088Z" fill="white"/>
<path d="M214.367 209.406C214.096 209.763 213.7 210.007 213.259 210.088V211.336C213.666 211.292 214.061 211.165 214.417 210.963C214.774 210.761 215.086 210.488 215.333 210.161L214.367 209.406Z" fill="white"/>
<path d="M208.567 205.437H207.333V211.176H208.567V205.437Z" fill="white"/>
<path d="M196.807 209.552C196.682 209.25 196.627 208.923 196.648 208.597V205.437H195.409V208.71C195.398 209.061 195.444 209.411 195.546 209.747C195.642 210.032 195.798 210.293 196.003 210.514C196.186 210.719 196.404 210.89 196.648 211.019C196.89 211.152 197.153 211.245 197.425 211.291V210.03C197.169 209.95 196.949 209.78 196.807 209.552Z" fill="white"/>
<path d="M198.897 208.601C198.918 208.933 198.86 209.265 198.728 209.57C198.584 209.794 198.363 209.958 198.105 210.03V211.291C198.38 211.247 198.644 211.157 198.888 211.023C199.13 210.89 199.348 210.718 199.535 210.514C199.741 210.294 199.898 210.033 199.994 209.747C200.093 209.41 200.14 209.06 200.131 208.71V205.437H198.897V208.601Z" fill="white"/>
<path d="M202.886 205.437H201.651V211.176H202.886V205.437Z" fill="white"/>
<defs>
<clipPath id="clip0_1968_5896">
<rect width="220" height="220" fill="white"/>
</clipPath>
<clipPath id="clip1_1968_5896">
<rect width="237.333" height="230" fill="white" transform="translate(-9 -4.93579)"/>
</clipPath>
<clipPath id="clip2_1968_5896">
<rect width="220" height="220" fill="white"/>
</clipPath>
<clipPath id="clip3_1968_5896">
<rect width="296.667" height="230" fill="white" transform="matrix(4.37114e-08 1 1 -4.37114e-08 0 -37.9358)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.
+59
View File
@@ -0,0 +1,59 @@
<svg width="230" height="231" viewBox="0 0 230 231" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1959_5738)">
<g clip-path="url(#clip1_1959_5738)">
<path d="M25.6667 0.532104C25.6667 10.2224 25.6667 193.236 25.6667 283.532" stroke="#9B9B9B" stroke-width="0.166667"/>
<path d="M55.3333 0.532104C55.3333 10.2224 55.3333 193.236 55.3333 283.532" stroke="#9B9B9B" stroke-width="0.5"/>
<path d="M85 0.532104C85 10.2224 85 193.236 85 283.532" stroke="#9B9B9B" stroke-width="0.166667"/>
<path d="M114.667 0.532104C114.667 10.2224 114.667 193.236 114.667 283.532" stroke="#9B9B9B" stroke-width="0.5"/>
<path d="M144.333 0.532104C144.333 10.2224 144.333 193.236 144.333 283.532" stroke="#9B9B9B" stroke-width="0.166667"/>
<path d="M174 0.532104C174 7.52982 174 139.691 174 204.897" stroke="#9B9B9B" stroke-width="0.666667"/>
<path d="M203.667 0.532104C203.667 7.52982 203.667 139.691 203.667 204.897" stroke="#9B9B9B" stroke-width="0.166667"/>
</g>
</g>
<g clip-path="url(#clip2_1959_5738)">
<g clip-path="url(#clip3_1959_5738)">
<path d="M230 26.8655C222.125 26.8655 73.3852 26.8655 1.60933e-06 26.8655" stroke="#9B9B9B" stroke-width="0.166667"/>
<path d="M230 56.5321C222.125 56.5321 73.3852 56.5321 1.60933e-06 56.5321" stroke="#9B9B9B" stroke-width="0.5"/>
<path d="M230 86.1987C222.125 86.1987 73.3852 86.1987 1.60933e-06 86.1987" stroke="#9B9B9B" stroke-width="0.166667"/>
<path d="M230 115.865C222.125 115.865 73.3852 115.865 1.60933e-06 115.865" stroke="#9B9B9B" stroke-width="0.5"/>
<path d="M230 145.532C222.125 145.532 73.3852 145.532 1.60933e-06 145.532" stroke="#9B9B9B" stroke-width="0.166667"/>
<path d="M230 175.199C222.125 175.199 73.3852 175.199 1.60933e-06 175.199" stroke="#9B9B9B" stroke-width="0.5"/>
<path d="M230 204.865C222.125 204.865 73.3852 204.865 1.60933e-06 204.865" stroke="#9B9B9B" stroke-width="0.166667"/>
</g>
</g>
<path opacity="0.9" fill-rule="evenodd" clip-rule="evenodd" d="M168.09 211.474L173.745 216.915L177.176 217.249L172.701 213.314L168.09 211.474Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M173.666 216.968L168.014 211.51L164.531 217.778L168.417 223.576L173.666 216.968Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M168.493 223.59L173.754 217.002L177.197 217.309L173.063 221.932L168.493 223.59Z" fill="white"/>
<path d="M193.734 214.702H192.223L193.761 216.878L194.53 215.835L193.734 214.702Z" fill="white"/>
<path d="M190.552 217.907L186.723 214.516V216.154L191.787 220.626V214.702H190.552V217.907Z" fill="white"/>
<path d="M196.243 214.702L194.198 217.501V220.442H195.436V217.902L197.772 214.702H196.243Z" fill="white"/>
<path d="M182.253 215.777L183.549 218.277H183.064L182.448 219.482H184.161L184.662 220.442H186.045L182.953 214.438L182.253 215.777Z" fill="white"/>
<path d="M186.723 220.442V220.444H187.957V218.148L186.723 217.064V220.442Z" fill="white"/>
<path d="M181.881 216.519L179.864 220.442H181.243L182.568 217.867L181.881 216.519Z" fill="white"/>
<path d="M213.778 218.099C213.976 218.05 214.162 217.962 214.326 217.842C214.548 217.681 214.727 217.467 214.847 217.219C214.967 216.972 215.024 216.699 215.013 216.424C215.02 216.157 214.962 215.893 214.843 215.654C214.724 215.416 214.548 215.21 214.33 215.057C214.157 214.928 213.961 214.834 213.752 214.78C213.468 214.719 213.178 214.693 212.887 214.702H212.577V215.921H213.02C213.212 215.906 213.404 215.936 213.581 216.01C213.654 216.051 213.713 216.113 213.752 216.187C213.792 216.261 213.809 216.345 213.803 216.428C213.809 216.525 213.786 216.622 213.736 216.706C213.686 216.79 213.612 216.856 213.523 216.896C213.347 216.958 213.16 216.984 212.974 216.973H212.577V218.179H213.02C213.275 218.184 213.53 218.157 213.778 218.099Z" fill="white"/>
<path d="M198.35 215.739C197.938 216.265 197.717 216.915 197.723 217.583C197.724 218.045 197.831 218.5 198.035 218.914C198.239 219.327 198.536 219.689 198.902 219.97C199.345 220.309 199.869 220.525 200.422 220.597V219.351C199.962 219.254 199.556 218.983 199.288 218.596C199.111 218.342 199.003 218.046 198.975 217.737C198.947 217.429 199 217.118 199.128 216.836C199.23 216.602 199.381 216.392 199.571 216.222C199.817 216.012 200.111 215.866 200.427 215.799V214.552C200.019 214.6 199.624 214.73 199.267 214.934C198.911 215.138 198.599 215.412 198.35 215.739Z" fill="white"/>
<path d="M202.202 216.499L203.168 215.73C202.931 215.408 202.629 215.138 202.282 214.939C201.922 214.738 201.526 214.609 201.116 214.56V215.808C201.552 215.894 201.94 216.141 202.202 216.499Z" fill="white"/>
<path d="M223.363 216.499L224.331 215.73C224.093 215.409 223.792 215.139 223.445 214.939C223.085 214.738 222.689 214.609 222.279 214.56V215.808C222.714 215.895 223.101 216.141 223.363 216.499Z" fill="white"/>
<path d="M213.811 218.562C213.82 218.602 213.825 218.643 213.825 218.684C213.835 218.79 213.808 218.897 213.749 218.985C213.69 219.074 213.603 219.14 213.501 219.172C213.332 219.218 213.158 219.239 212.983 219.232H212.586V220.437H212.991C213.257 220.442 213.522 220.419 213.783 220.366C213.978 220.322 214.162 220.24 214.326 220.125C214.549 219.968 214.73 219.758 214.853 219.513C214.981 219.264 215.048 218.989 215.048 218.709C215.05 218.452 214.999 218.198 214.897 217.962C214.62 218.287 214.234 218.501 213.811 218.562Z" fill="white"/>
<path d="M219.511 215.739C219.099 216.264 218.878 216.915 218.886 217.583C218.887 218.045 218.993 218.5 219.198 218.914C219.402 219.328 219.699 219.689 220.065 219.97C220.508 220.31 221.032 220.526 221.586 220.597V219.351C221.124 219.254 220.717 218.984 220.449 218.596C220.272 218.341 220.165 218.045 220.137 217.737C220.109 217.429 220.162 217.118 220.289 216.836C220.39 216.601 220.541 216.392 220.732 216.222C220.977 216.012 221.272 215.866 221.588 215.799V214.552C221.18 214.6 220.785 214.73 220.428 214.934C220.072 215.138 219.76 215.412 219.511 215.739Z" fill="white"/>
<path d="M201.109 219.354V220.601C201.517 220.558 201.911 220.431 202.267 220.229C202.623 220.027 202.935 219.754 203.181 219.427L202.206 218.671C201.937 219.026 201.547 219.269 201.109 219.354Z" fill="white"/>
<path d="M223.367 218.671C223.096 219.029 222.701 219.272 222.259 219.354V220.601C222.667 220.557 223.061 220.43 223.418 220.228C223.774 220.026 224.086 219.754 224.333 219.427L223.367 218.671Z" fill="white"/>
<path d="M217.568 214.702H216.333V220.442H217.568V214.702Z" fill="white"/>
<path d="M205.808 218.817C205.682 218.515 205.627 218.189 205.648 217.862V214.702H204.409V217.975C204.398 218.326 204.445 218.676 204.547 219.012C204.642 219.298 204.798 219.559 205.003 219.779C205.187 219.984 205.405 220.155 205.648 220.284C205.89 220.418 206.153 220.51 206.426 220.557V219.296C206.169 219.216 205.95 219.046 205.808 218.817Z" fill="white"/>
<path d="M207.897 217.867C207.918 218.198 207.86 218.53 207.729 218.835C207.585 219.06 207.363 219.224 207.106 219.296V220.557C207.38 220.513 207.645 220.422 207.888 220.289C208.13 220.155 208.349 219.983 208.535 219.779C208.742 219.56 208.898 219.298 208.994 219.012C209.094 218.676 209.14 218.326 209.131 217.975V214.702H207.897V217.867Z" fill="white"/>
<path d="M211.886 214.702H210.651V220.442H211.886V214.702Z" fill="white"/>
<defs>
<clipPath id="clip0_1959_5738">
<rect width="230" height="230" fill="white" transform="translate(0 0.532104)"/>
</clipPath>
<clipPath id="clip1_1959_5738">
<rect width="237.333" height="230" fill="white" transform="translate(-4 0.532104)"/>
</clipPath>
<clipPath id="clip2_1959_5738">
<rect width="230" height="230" fill="white" transform="translate(0 0.532104)"/>
</clipPath>
<clipPath id="clip3_1959_5738">
<rect width="296.667" height="230" fill="white" transform="translate(230 -32.4679) rotate(90)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.
+31
View File
@@ -0,0 +1,31 @@
<svg width="2551" height="2535" viewBox="0 0 2551 2535" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_6167_34126)">
<path d="M32.4251 1506.66H24.1299V2540.77H32.4251V1506.66Z" fill="#898989"/>
<path d="M32.4251 42.3779H24.1299V456.047H32.4251V42.3779Z" fill="#898989"/>
<path d="M31.0301 576.22C9.10446 576.22 2.49023 565.276 2.49023 548.566V530.252H59.5699V548.493C59.5699 565.166 54.7828 576.22 31.0301 576.22ZM31.0301 565.276C46.8896 565.276 50.4343 558.855 50.4343 548.165V540.868H11.7721V548.165C11.7721 559.073 15.9745 565.166 31.0301 565.166V565.276Z" fill="#898989"/>
<path d="M24.6762 585.557C42.6187 585.557 47.7346 593.364 47.7346 606.826C47.7346 620.288 42.6187 628.169 24.6762 628.169C6.73373 628.169 1.6543 620.398 1.6543 606.826C1.6543 593.255 6.62411 585.557 24.6762 585.557ZM24.6762 617.589C36.2237 617.589 39.8414 614.889 39.8414 606.826C39.8414 598.763 36.1872 596.173 24.6762 596.173C13.1652 596.173 9.65715 598.873 9.65715 606.826C9.65715 614.779 13.1652 617.589 24.6762 617.589Z" fill="#898989"/>
<path d="M46.8211 677.095H2.49478V667.901L8.1589 667.025C3.40835 664.472 1.6543 659.473 1.6543 653.308C1.6543 642.144 7.09916 638.423 16.2714 638.423H46.8211V648.711H19.341C13.1652 648.711 10.2053 650.097 10.2053 656.409C9.87634 658.717 10.4203 661.064 11.731 662.993C13.0417 664.922 15.0252 666.295 17.2946 666.843H46.8211V677.095Z" fill="#898989"/>
<path d="M24.6752 731.924C12.1045 731.924 1.65332 728.568 1.65332 714.047C1.65332 705.583 4.61328 701.899 9.21766 700.147L2.4938 699.345V690.078H65.0915V700.33H40.6444C44.591 702.117 47.7337 705.985 47.7337 714.047C47.7337 728.568 38.598 731.924 24.6752 731.924ZM24.6752 721.344C33.2628 721.344 39.8404 720.031 39.8404 711.238C39.8404 702.446 34.2494 699.746 24.6752 699.746C15.101 699.746 9.65617 702.227 9.65617 711.238C9.65617 720.25 16.7089 721.344 24.6752 721.344Z" fill="#898989"/>
<path d="M12.8344 742.434H65.0905V752.795H15.3193C11.19 752.795 10.2033 753.889 10.2033 756.662C10.2129 757.583 10.3232 758.501 10.5322 759.398H2.49284C2.13902 757.768 1.96744 756.104 1.98124 754.436C1.98124 745.498 5.27009 742.434 12.8344 742.434Z" fill="#898989"/>
<path d="M22.7031 803.865L20.8394 774.679C13.5309 774.934 9.87662 778.546 9.87662 787.411C9.86363 792.433 10.8965 797.402 12.9097 802.004H4.50484C2.37394 796.864 1.36487 791.33 1.54488 785.769C1.54488 772.271 6.66086 764.5 24.5668 764.5C42.4727 764.5 47.6252 772.271 47.6252 785.769C47.6252 801.092 38.8915 804.303 28.4403 804.303C26.5196 804.301 24.6018 804.155 22.7031 803.865ZM29.6462 794.744C35.1276 794.744 39.8416 793.613 39.8416 785.733C39.8416 777.853 35.895 775.116 28.148 774.788L29.6462 794.744Z" fill="#898989"/>
<path d="M3.73774 833.962H13.3485C11.7625 839.29 10.9623 844.821 10.9732 850.379C10.9732 858.26 12.9465 861.324 18.6837 861.324C24.4209 861.324 25.5903 859.427 27.8925 850.051C30.7793 838.084 33.9951 834.29 44.1174 834.29C55.7015 834.29 60.4886 841.368 60.4886 854.502C60.5787 859.446 59.8755 864.373 58.4056 869.095H49.2699C50.6728 864.493 51.3991 859.713 51.426 854.903C51.426 848.008 50.0373 844.87 44.3367 844.87C40.0612 844.87 38.6726 846.184 36.4435 855.231C33.2642 868.511 29.3907 872.05 18.5375 872.05C7.68436 872.05 1.65481 865.556 1.65481 850.708C1.63257 845.06 2.33254 839.433 3.73774 833.962Z" fill="#898989"/>
<path d="M46.8185 882.886V893.138H2.49219V882.886H46.8185ZM59.243 882.229C64.2493 882.229 64.7609 883.47 64.7609 888.067C64.7609 892.664 64.2493 893.868 59.243 893.868C54.2366 893.868 53.652 892.664 53.652 888.067C53.652 883.47 54.3828 882.229 59.3161 882.229H59.243Z" fill="#898989"/>
<path d="M65.0881 945.53H2.49046V936.409L8.99506 935.424C4.71957 933.709 1.68652 929.587 1.68652 921.634C1.68652 907.04 10.8222 903.647 24.7084 903.647C37.2426 903.647 47.7669 907.113 47.7669 921.634C47.7669 929.915 44.9531 933.527 40.4583 935.351H65.0881V945.53ZM24.8911 935.752C33.9537 935.752 40.0564 933.381 40.0564 924.406C40.0564 915.431 32.7478 914.337 24.8911 914.337C16.3402 914.337 9.87209 915.541 9.87209 924.406C9.87209 933.272 15.1342 935.752 24.6719 935.752H24.8911Z" fill="#898989"/>
<path d="M22.7031 995.508L20.8394 966.321C13.5309 966.577 9.87662 970.188 9.87662 979.054C9.86363 984.076 10.8965 989.045 12.9097 993.647H4.50484C2.37394 988.507 1.36487 982.972 1.54488 977.412C1.54488 963.95 6.66086 956.143 24.5668 956.143C42.4727 956.143 47.6252 963.95 47.6252 977.412C47.6252 992.771 38.8915 995.945 28.4403 995.945C26.5196 995.944 24.6018 995.797 22.7031 995.508ZM29.6462 986.387C35.1276 986.387 39.8416 985.256 39.8416 977.376C39.8416 969.495 35.895 966.759 28.148 966.431L29.6462 986.387Z" fill="#898989"/>
<path d="M65.0891 1045.82H2.49144V1036.7L8.99604 1035.71C4.72054 1034 1.6875 1029.91 1.6875 1021.92C1.6875 1007.33 10.8232 1003.93 24.7094 1003.93C37.2436 1003.93 47.7679 1007.58 47.7679 1021.92C47.7679 1030.24 44.9541 1033.85 40.4593 1035.64H65.0891V1045.82ZM24.8921 1036.04C33.9547 1036.04 40.0573 1033.67 40.0573 1024.73C40.0573 1015.79 32.7488 1014.62 24.8921 1014.62C16.3411 1014.62 9.87307 1015.86 9.87307 1024.73C9.87307 1033.59 15.1352 1036.04 24.6729 1036.04H24.8921Z" fill="#898989"/>
<path d="M59.5719 1079.7V1099.37C59.5719 1116.44 53.9808 1121.77 40.1677 1121.77C25.2583 1121.77 20.0692 1115.38 20.0692 1099.37V1092.07H2.49219V1079.59L59.5719 1079.7ZM30.1185 1097.54C30.1185 1105.97 32.3476 1108.85 39.9119 1108.85C47.4762 1108.85 49.7054 1105.9 49.7054 1097.54V1092.07H30.1185V1097.54Z" fill="#898989"/>
<path d="M13.3486 1167.7H2.38574V1131.77H59.5751V1166.57H48.6123V1144.24H36.9551V1165.04H26.5039V1144.24H13.3486V1167.7Z" fill="#898989"/>
<path d="M59.5699 1177.88V1190.36H2.49023V1177.88H59.5699Z" fill="#898989"/>
<path d="M3.73772 1222.28H13.3485C11.763 1227.62 10.9629 1233.16 10.9732 1238.73C10.9732 1246.61 12.9465 1249.68 18.6837 1249.68C24.4209 1249.68 25.5903 1247.78 27.8924 1238.44C30.7793 1226.44 33.9951 1222.68 44.1174 1222.68C55.7014 1222.68 60.4885 1229.72 60.4885 1242.85C60.5774 1247.8 59.8743 1252.72 58.4056 1257.45H49.2699C50.6728 1252.83 51.399 1248.04 51.4259 1243.22C51.4259 1236.32 50.0373 1233.22 44.3367 1233.22C40.0612 1233.22 38.6725 1234.54 36.4434 1243.55C33.2642 1256.86 29.3907 1260.4 18.5375 1260.4C7.68434 1260.4 1.65479 1253.91 1.65479 1239.06C1.63302 1233.4 2.33295 1227.76 3.73772 1222.28Z" fill="#898989"/>
<path d="M32.9313 1310.39H2.49121V1300.02H30.0444C36.2932 1300.02 39.1801 1298.53 39.1801 1292.36C39.5523 1289.96 39.0085 1287.5 37.6557 1285.47C36.303 1283.45 34.2394 1282 31.8715 1281.42H2.63738V1271.17H65.0889V1281.78H41.1534C45.8309 1284.26 47.7311 1289.66 47.7311 1295.76C47.7311 1306.85 42.2862 1310.39 32.9313 1310.39Z" fill="#898989"/>
<path d="M22.704 1360.11L20.8403 1330.92C13.5318 1331.14 9.87752 1334.75 9.87752 1343.65C9.87193 1348.67 10.9045 1353.64 12.9106 1358.25H4.50574C2.37511 1353.09 1.36616 1347.55 1.54578 1341.98C1.54578 1328.51 6.66176 1320.71 24.5677 1320.71C42.4736 1320.71 47.6261 1328.51 47.6261 1341.98C47.6261 1357.33 38.8924 1360.55 28.4412 1360.55C26.5204 1360.55 24.6025 1360.4 22.704 1360.11ZM29.6471 1351.1C35.1285 1351.1 39.8425 1349.93 39.8425 1342.05C39.8425 1334.17 35.8959 1331.47 28.1489 1331.1L29.6471 1351.1Z" fill="#898989"/>
<path d="M22.7041 1407.97L20.8404 1378.78C13.5319 1379.04 9.8776 1382.65 9.8776 1391.52C9.86033 1396.54 10.8934 1401.51 12.9106 1406.11H4.50582C2.37492 1400.97 1.36585 1395.44 1.54586 1389.88C1.54586 1376.41 6.66184 1368.61 24.5678 1368.61C42.4737 1368.61 47.6262 1376.41 47.6262 1389.88C47.6262 1405.23 38.8925 1408.41 28.4413 1408.41C26.5206 1408.41 24.6028 1408.26 22.7041 1407.97ZM29.6472 1398.85C35.1286 1398.85 39.8426 1397.72 39.8426 1389.84C39.8426 1381.96 35.896 1379.22 28.1489 1378.89L29.6472 1398.85Z" fill="#898989"/>
<path d="M38.6723 1429.79H17.2948C11.521 1429.79 9.98625 1430.77 9.98625 1435.3C9.94017 1436.66 10.0753 1438.02 10.3882 1439.35H2.42191C2.14447 1437.52 1.99792 1435.68 1.9834 1433.84C1.9834 1423.22 4.79719 1419.54 16.6005 1419.54H38.8184V1413.37H46.8213V1419.54H57.4187V1429.79H46.8213V1439.57H38.6723V1429.79Z" fill="#898989"/>
<rect x="1.41332" y="0.844964" width="2548.17" height="2533" rx="21.0064" stroke="#D9D9D9" stroke-width="1.82665"/>
</g>
<defs>
<clipPath id="clip0_6167_34126">
<rect width="2551" height="2535" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.
+52
View File
@@ -0,0 +1,52 @@
<svg width="332" height="332" viewBox="0 0 332 332" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.875" y="0.875" width="330.25" height="330.25" rx="4.125" fill="url(#paint0_radial_1968_5984)" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M16 1C16 12.2996 16 225.708 16 331" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M46 1C46 12.2996 46 225.708 46 331" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M76 1C76 12.2996 76 225.708 76 331" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M106 1C106 12.2996 106 225.708 106 331" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M136 1C136 12.2996 136 225.708 136 331" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M166 1C166 12.2996 166 225.708 166 331" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M196 1C196 12.2996 196 225.708 196 331" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M226 1C226 12.2996 226 225.708 226 331" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M256 1C256 12.2996 256 225.708 256 331" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M286 1C286 11.5805 286 211.409 286 310" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M316 1C316 11.5805 316 211.409 316 310" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M331 16C319.7 16 106.292 16 1.00001 16" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M331 46C319.7 46 106.292 46 1.00001 46" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M331 76C319.7 76 106.292 76 1.00001 76" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M331 106C319.7 106 106.292 106 1.00001 106" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M331 136C319.7 136 106.292 136 1.00001 136" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M331 166C319.7 166 106.292 166 1.00001 166" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M331 196C319.7 196 106.292 196 1.00001 196" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M331 226C319.7 226 106.292 226 1.00001 226" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M331 256C319.7 256 106.292 256 1.00001 256" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M331 286C319.7 286 106.292 286 1.00001 286" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M262 316C253.063 316 84.2763 316 1 316" stroke="#9B9B9B" stroke-width="0.25"/>
<path opacity="0.9" fill-rule="evenodd" clip-rule="evenodd" d="M270.09 314.442L275.745 319.883L279.176 320.217L274.701 316.281L270.09 314.442Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M275.666 319.936L270.014 314.477L266.531 320.745L270.417 326.544L275.666 319.936Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M270.493 326.558L275.754 319.97L279.197 320.277L275.063 324.9L270.493 326.558Z" fill="white"/>
<path d="M295.734 317.67H294.223L295.761 319.846L296.53 318.803L295.734 317.67Z" fill="white"/>
<path d="M292.552 320.875L288.723 317.484V319.122L293.787 323.594V317.67H292.552V320.875Z" fill="white"/>
<path d="M298.243 317.67L296.198 320.469V323.41H297.436V320.87L299.772 317.67H298.243Z" fill="white"/>
<path d="M284.253 318.745L285.549 321.245H285.064L284.448 322.45H286.161L286.662 323.41H288.045L284.953 317.406L284.253 318.745Z" fill="white"/>
<path d="M288.723 323.41V323.412H289.957V321.116L288.723 320.032V323.41Z" fill="white"/>
<path d="M283.881 319.487L281.864 323.41H283.243L284.568 320.835L283.881 319.487Z" fill="white"/>
<path d="M315.778 321.067C315.976 321.018 316.162 320.931 316.326 320.81C316.548 320.649 316.727 320.435 316.847 320.187C316.967 319.94 317.024 319.667 317.013 319.392C317.02 319.125 316.962 318.861 316.843 318.622C316.724 318.384 316.548 318.179 316.33 318.025C316.157 317.896 315.961 317.802 315.752 317.748C315.468 317.688 315.178 317.661 314.887 317.67H314.577V318.889H315.02C315.212 318.874 315.404 318.904 315.581 318.978C315.654 319.019 315.713 319.081 315.752 319.155C315.792 319.229 315.809 319.313 315.803 319.396C315.809 319.493 315.786 319.59 315.736 319.674C315.686 319.758 315.612 319.824 315.523 319.864C315.347 319.926 315.16 319.952 314.974 319.941H314.577V321.147H315.02C315.275 321.152 315.53 321.125 315.778 321.067Z" fill="white"/>
<path d="M300.35 318.707C299.938 319.233 299.717 319.883 299.723 320.551C299.724 321.013 299.831 321.468 300.035 321.882C300.239 322.295 300.536 322.657 300.902 322.938C301.345 323.277 301.869 323.493 302.422 323.565V322.319C301.962 322.222 301.556 321.951 301.288 321.564C301.111 321.31 301.003 321.014 300.975 320.705C300.947 320.397 301 320.086 301.128 319.804C301.23 319.57 301.381 319.36 301.571 319.19C301.817 318.98 302.111 318.835 302.427 318.767V317.52C302.019 317.568 301.624 317.698 301.267 317.902C300.911 318.106 300.599 318.38 300.35 318.707Z" fill="white"/>
<path d="M304.202 319.467L305.168 318.698C304.931 318.376 304.629 318.107 304.282 317.907C303.922 317.706 303.526 317.577 303.116 317.528V318.776C303.552 318.862 303.939 319.109 304.202 319.467Z" fill="white"/>
<path d="M325.363 319.467L326.331 318.698C326.093 318.377 325.792 318.107 325.445 317.907C325.085 317.706 324.689 317.577 324.279 317.528V318.776C324.714 318.863 325.101 319.109 325.363 319.467Z" fill="white"/>
<path d="M315.811 321.53C315.82 321.57 315.825 321.611 315.825 321.652C315.835 321.758 315.808 321.865 315.749 321.953C315.69 322.042 315.603 322.108 315.501 322.14C315.332 322.186 315.158 322.207 314.983 322.2H314.586V323.405H314.991C315.257 323.41 315.522 323.387 315.783 323.334C315.978 323.29 316.162 323.208 316.326 323.093C316.549 322.936 316.73 322.726 316.853 322.481C316.981 322.232 317.048 321.957 317.048 321.677C317.05 321.42 316.999 321.166 316.897 320.93C316.62 321.255 316.234 321.469 315.811 321.53Z" fill="white"/>
<path d="M321.511 318.707C321.099 319.232 320.878 319.883 320.886 320.551C320.887 321.013 320.993 321.468 321.198 321.882C321.402 322.296 321.699 322.657 322.065 322.938C322.508 323.278 323.032 323.494 323.586 323.565V322.319C323.124 322.222 322.717 321.952 322.449 321.564C322.272 321.309 322.165 321.013 322.137 320.705C322.109 320.397 322.162 320.086 322.289 319.804C322.39 319.569 322.541 319.36 322.732 319.19C322.977 318.98 323.272 318.834 323.588 318.767V317.52C323.18 317.568 322.785 317.698 322.428 317.902C322.072 318.106 321.76 318.38 321.511 318.707Z" fill="white"/>
<path d="M303.109 322.322V323.569C303.517 323.526 303.911 323.399 304.267 323.197C304.623 322.995 304.935 322.722 305.181 322.395L304.206 321.639C303.937 321.994 303.547 322.237 303.109 322.322Z" fill="white"/>
<path d="M325.367 321.639C325.096 321.997 324.701 322.24 324.259 322.322V323.569C324.667 323.525 325.061 323.398 325.418 323.196C325.774 322.994 326.086 322.722 326.333 322.395L325.367 321.639Z" fill="white"/>
<path d="M319.568 317.67H318.333V323.41H319.568V317.67Z" fill="white"/>
<path d="M307.807 321.785C307.682 321.483 307.627 321.157 307.648 320.83V317.67H306.409V320.943C306.398 321.294 306.445 321.644 306.546 321.98C306.642 322.266 306.798 322.527 307.003 322.747C307.186 322.952 307.405 323.123 307.648 323.252C307.89 323.386 308.153 323.478 308.426 323.525V322.264C308.169 322.184 307.949 322.014 307.807 321.785Z" fill="white"/>
<path d="M309.897 320.835C309.918 321.166 309.86 321.498 309.729 321.803C309.585 322.028 309.363 322.192 309.106 322.264V323.525C309.38 323.481 309.645 323.39 309.888 323.257C310.13 323.123 310.349 322.951 310.535 322.747C310.742 322.528 310.898 322.266 310.994 321.98C311.094 321.644 311.14 321.294 311.131 320.943V317.67H309.897V320.835Z" fill="white"/>
<path d="M313.886 317.67H312.651V323.41H313.886V317.67Z" fill="white"/>
<defs>
<radialGradient id="paint0_radial_1968_5984" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(166 166) rotate(90) scale(198.803)">
<stop stop-color="white" stop-opacity="0.08"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</radialGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.
+60
View File
@@ -0,0 +1,60 @@
<svg width="432" height="432" viewBox="0 0 432 432" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.875" y="0.875" width="430.25" height="430.25" rx="4.125" fill="url(#paint0_radial_1983_6404)" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M6 1C6 15.7237 6 293.802 6 431" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M36 1C36 15.7237 36 293.802 36 431" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M66 1C66 15.7237 66 293.802 66 431" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M96 1C96 15.7237 96 293.802 96 431" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M126 1C126 15.7237 126 293.802 126 431" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M156 1C156 15.7237 156 293.802 156 431" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M186 1C186 15.7237 186 293.802 186 431" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M216 1C216 15.7237 216 293.802 216 431" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M246 1C246 15.7237 246 293.802 246 431" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M276 1C276 15.7237 276 293.802 276 431" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M306 1C306 15.7237 306 293.802 306 431" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M336 1C336 15.7237 336 293.802 336 431" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M366 1C366 15.0047 366 279.502 366 410" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M396 1C396 15.0047 396 279.502 396 410" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M426 1C426 15.0047 426 279.502 426 410" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M431 6C416.276 6 138.198 5.99999 1.00001 5.99998" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M431 36C416.276 36 138.198 36 1.00001 36" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M431 66C416.276 66 138.198 66 1.00001 66" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M431 96C416.276 96 138.198 96 1.00001 96" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M431 126C416.276 126 138.198 126 1.00001 126" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M431 156C416.276 156 138.198 156 1.00001 156" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M431 186C416.276 186 138.198 186 1.00001 186" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M431 216C416.276 216 138.198 216 1.00001 216" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M431 246C416.276 246 138.198 246 1.00001 246" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M431 276C416.276 276 138.198 276 1.00001 276" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M431 306C416.276 306 138.198 306 1.00001 306" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M431 336C416.276 336 138.198 336 1.00001 336" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M431 366C416.276 366 138.198 366 1.00001 366" stroke="#9B9B9B" stroke-width="0.25"/>
<path d="M431 396C416.276 396 138.198 396 1.00001 396" stroke="#9B9B9B" stroke-width="0.75"/>
<path d="M362 426C349.639 426 116.183 426 1.00001 426" stroke="#9B9B9B" stroke-width="0.25"/>
<path opacity="0.9" fill-rule="evenodd" clip-rule="evenodd" d="M370.09 414.442L375.745 419.883L379.176 420.217L374.701 416.281L370.09 414.442Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M375.666 419.936L370.014 414.477L366.531 420.745L370.417 426.544L375.666 419.936Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M370.493 426.558L375.754 419.97L379.197 420.277L375.063 424.9L370.493 426.558Z" fill="white"/>
<path d="M395.734 417.67H394.223L395.761 419.846L396.53 418.802L395.734 417.67Z" fill="white"/>
<path d="M392.552 420.874L388.723 417.484V419.122L393.786 423.593V417.67H392.552V420.874Z" fill="white"/>
<path d="M398.243 417.67L396.198 420.469V423.409H397.437V420.87L399.772 417.67H398.243Z" fill="white"/>
<path d="M384.253 418.745L385.549 421.244H385.064L384.448 422.45H386.161L386.662 423.409H388.045L384.953 417.406L384.253 418.745Z" fill="white"/>
<path d="M388.723 423.409V423.412H389.957V421.116L388.723 420.032V423.409Z" fill="white"/>
<path d="M383.881 419.487L381.864 423.41H383.243L384.568 420.835L383.881 419.487Z" fill="white"/>
<path d="M415.778 421.067C415.976 421.018 416.162 420.931 416.326 420.81C416.548 420.649 416.727 420.435 416.847 420.187C416.967 419.94 417.024 419.667 417.013 419.392C417.02 419.125 416.962 418.861 416.843 418.622C416.724 418.384 416.548 418.179 416.33 418.025C416.157 417.896 415.961 417.802 415.752 417.748C415.468 417.688 415.178 417.661 414.887 417.67H414.577V418.889H415.02C415.212 418.874 415.404 418.904 415.581 418.978C415.654 419.019 415.713 419.081 415.752 419.155C415.792 419.229 415.809 419.313 415.803 419.396C415.809 419.493 415.786 419.59 415.736 419.674C415.686 419.758 415.612 419.824 415.523 419.864C415.347 419.926 415.16 419.952 414.974 419.941H414.577V421.147H415.02C415.275 421.152 415.53 421.125 415.778 421.067Z" fill="white"/>
<path d="M400.35 418.707C399.938 419.232 399.717 419.883 399.723 420.551C399.724 421.012 399.831 421.467 400.035 421.881C400.239 422.295 400.536 422.656 400.902 422.937C401.345 423.276 401.869 423.493 402.422 423.564V422.319C401.962 422.221 401.556 421.951 401.288 421.563C401.111 421.309 401.003 421.013 400.975 420.705C400.947 420.396 401 420.086 401.128 419.804C401.23 419.569 401.381 419.36 401.571 419.19C401.817 418.98 402.111 418.834 402.427 418.767V417.519C402.019 417.567 401.624 417.697 401.267 417.901C400.911 418.105 400.599 418.379 400.35 418.707Z" fill="white"/>
<path d="M404.202 419.467L405.168 418.698C404.931 418.376 404.629 418.107 404.282 417.907C403.922 417.706 403.526 417.577 403.116 417.528V418.776C403.552 418.862 403.94 419.109 404.202 419.467Z" fill="white"/>
<path d="M425.363 419.467L426.331 418.698C426.093 418.377 425.792 418.107 425.445 417.907C425.085 417.706 424.689 417.577 424.279 417.528V418.776C424.714 418.863 425.101 419.109 425.363 419.467Z" fill="white"/>
<path d="M415.811 421.53C415.82 421.57 415.825 421.611 415.825 421.652C415.835 421.758 415.808 421.864 415.749 421.953C415.69 422.042 415.603 422.108 415.501 422.14C415.332 422.186 415.158 422.206 414.983 422.199H414.586V423.405H414.991C415.257 423.41 415.522 423.386 415.783 423.334C415.978 423.29 416.162 423.208 416.326 423.093C416.549 422.935 416.73 422.725 416.853 422.481C416.981 422.232 417.048 421.956 417.048 421.676C417.05 421.42 416.999 421.165 416.897 420.93C416.62 421.255 416.234 421.469 415.811 421.53Z" fill="white"/>
<path d="M421.511 418.707C421.099 419.232 420.878 419.883 420.886 420.551C420.887 421.012 420.993 421.468 421.198 421.881C421.402 422.295 421.699 422.656 422.065 422.937C422.508 423.277 423.032 423.493 423.586 423.564V422.319C423.124 422.221 422.717 421.951 422.449 421.563C422.272 421.309 422.165 421.013 422.137 420.705C422.109 420.396 422.162 420.086 422.289 419.804C422.39 419.569 422.541 419.359 422.732 419.19C422.977 418.979 423.272 418.834 423.588 418.767V417.519C423.18 417.567 422.785 417.697 422.428 417.901C422.072 418.105 421.76 418.379 421.511 418.707Z" fill="white"/>
<path d="M403.109 422.321V423.569C403.517 423.525 403.911 423.398 404.267 423.196C404.623 422.994 404.935 422.721 405.181 422.394L404.206 421.639C403.937 421.994 403.547 422.237 403.109 422.321Z" fill="white"/>
<path d="M425.367 421.639C425.096 421.996 424.701 422.24 424.259 422.321V423.569C424.667 423.525 425.061 423.398 425.418 423.196C425.774 422.994 426.086 422.721 426.333 422.394L425.367 421.639Z" fill="white"/>
<path d="M419.568 417.67H418.333V423.409H419.568V417.67Z" fill="white"/>
<path d="M407.808 421.785C407.682 421.483 407.627 421.156 407.648 420.83V417.67H406.409V420.943C406.398 421.294 406.445 421.644 406.547 421.98C406.642 422.265 406.798 422.527 407.003 422.747C407.187 422.952 407.405 423.123 407.648 423.252C407.89 423.386 408.153 423.478 408.426 423.525V422.264C408.169 422.184 407.95 422.014 407.808 421.785Z" fill="white"/>
<path d="M409.897 420.834C409.918 421.166 409.86 421.498 409.729 421.803C409.585 422.028 409.363 422.192 409.106 422.264V423.525C409.38 423.481 409.645 423.39 409.888 423.257C410.13 423.123 410.349 422.951 410.535 422.747C410.742 422.527 410.898 422.266 410.994 421.98C411.094 421.644 411.14 421.294 411.131 420.943V417.67H409.897V420.834Z" fill="white"/>
<path d="M413.886 417.67H412.651V423.409H413.886V417.67Z" fill="white"/>
<defs>
<radialGradient id="paint0_radial_1983_6404" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(216 216) rotate(90) scale(259.047)">
<stop stop-color="white" stop-opacity="0.08"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</radialGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

+561
View File
@@ -0,0 +1,561 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210.5mm"
height="210.5mm"
viewBox="0 0 210.5 210.5"
version="1.1"
id="svg886"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="ai3m.svg">
<defs
id="defs880" />
<sodipodi:namedview
id="base"
pagecolor="#7670ff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="0.70710678"
inkscape:cx="466.73093"
inkscape:cy="380.93745"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:lockguides="false"
showguides="false"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata883">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Layer 2"
transform="translate(0,-86.5)"
style="display:inline"
sodipodi:insensitive="true">
<path
id="path869"
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.04216461;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 167.32704,288.75984 v -3.83255 h 1.45715 v 7.07119 l -6.06232,-5.36476 v -1.94915 z m -9.42165,-2.52278 1.54352,2.96287 h -0.60107 l -0.76147,1.4617 h 2.12401 l 0.6004,1.1525 h 1.65752 l -3.73414,-7.16792 z m 44.62362,4.25408 v 1.50745 c 0.98166,-0.11796 1.8734,-0.62934 2.47099,-1.41702 l -1.14996,-0.94952 c -0.31636,0.44259 -0.78818,0.74942 -1.32103,0.85909 z m -40.17486,-27.16603 11.53897,-20.77294 18.70747,18.05526 -17.35981,21.92818 z m -5.69662,28.48902 1.6624,-3.19108 -0.82877,-1.59087 L 155,291.81413 Z m 22.56867,-7.05936 c 0.97028,0.11659 1.8532,0.61761 2.4509,1.39077 l -1.16155,0.93258 c -0.31614,-0.41985 -0.77457,-0.70996 -1.28935,-0.81591 z m 3.41542,0.17252 h 1.46761 v 4.19716 c 0,0.59031 0.3725,1.11637 0.92932,1.3124 v 1.50898 c -1.38222,-0.22635 -2.39693,-1.42074 -2.39693,-2.82138 z m 8.44048,6.88684 v -6.88684 h -1.43359 v 6.88684 z m 1.73019,-4.18512 c 0.35459,0 0.64204,-0.28746 0.64204,-0.64204 0,-0.35459 -0.28745,-0.64204 -0.64204,-0.64204 h -0.8869 v -1.41764 h 0.8869 c 1.13753,0 2.05968,0.92215 2.05968,2.05968 0,1.13753 -0.92215,2.05968 -2.05968,2.05968 h -0.8869 l -0.004,-1.41764 z m -30.09042,4.18512 h 1.49929 v -2.7357 l -1.49929,-1.32677 z m 33.23702,0 h 1.43359 v -6.88684 h -1.43359 z m -6.61958,-43.20436 -15.24232,-6.09042 18.63476,17.98509 11.51531,1.11097 z m -18.15413,43.20436 v -3.62964 l 2.39506,-3.2572 h 1.87519 l -2.78508,3.78762 v 3.09922 z m 0.39556,-5.55407 -0.92997,1.26472 -1.81565,-2.59749 h 1.814 z m 21.17859,-25.58701 11.46398,1.10603 -13.7034,15.36187 -15.13578,5.47973 z m 7.21757,28.12689 c 0.17089,0.85467 0.83896,1.52274 1.69364,1.69363 v 1.50644 c -1.6794,-0.19619 -3.0039,-1.52068 -3.20008,-3.20007 -0.23374,-2.00112 1.19897,-3.81283 3.20008,-4.04658 v 1.50643 c -1.16914,0.23375 -1.92739,1.37102 -1.69364,2.54015 z m -5.04526,0.93674 c 0,-0.30809 -0.0685,-0.61233 -0.20062,-0.89067 -0.35104,0.36232 -0.79749,0.61783 -1.28769,0.73699 0.0131,0.0502 0.0197,0.10183 0.0197,0.15368 0,0.33625 -0.2726,0.60885 -0.60885,0.60885 h -0.92882 v 1.4686 l 0.92882,-10e-6 c 1.14733,0 2.07744,-0.9301 2.07744,-2.07744 z m -14.38439,-0.10463 1.14995,0.94952 c -0.5976,0.78768 -1.48934,1.29906 -2.471,1.41702 v -1.50745 c 0.53286,-0.10967 1.00468,-0.4165 1.32105,-0.85909 z m -2.17951,0.86152 c -0.85468,-0.17089 -1.52275,-0.83896 -1.69364,-1.69363 -0.23376,-1.16913 0.52451,-2.3064 1.69364,-2.54015 v -1.50643 c -2.00111,0.23375 -3.43383,2.04546 -3.20007,4.04658 0.19617,1.67939 1.52067,3.00388 3.20007,3.20007 z m 9.99181,-1.36912 v -4.19716 h -1.46761 v 4.19716 c 0,0.62159 -0.41231,1.16778 -1.01011,1.3381 v 1.49533 c 1.41883,-0.19091 2.47772,-1.40182 2.47772,-2.83343 z m 14.16946,-2.86224 v -1.50744 c 0.97027,0.11659 1.85319,0.61761 2.45089,1.39077 l -1.16154,0.93258 c -0.31614,-0.41985 -0.77458,-0.70996 -1.28935,-0.81591 z"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccccccccccccccsccccscccccccccccccccccccccccccccccccccccccccccccsccsccccscccccccccccccccccccccccc" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Layer 1"
transform="translate(0,-86.5)"
style="display:none">
<path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.68079633,77.180801 H 219.81921"
id="path1596"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 219.81921,77.180801 V 296.31918"
id="path1594"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 219.81921,296.31918 H 0.68079633"
id="path1592"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.68079633,296.31918 V 77.180801"
id="rect1544"
inkscape:connector-curvature="0" />
<path
id="path5301"
d="M 9.681,296.31918 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 19.681,296.31918 V 77.180801"
id="path5303" />
<path
id="path5305"
d="M 29.681,296.31918 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 39.681,296.31918 V 77.180801"
id="path5307" />
<path
id="path5309"
d="M 49.681,296.31918 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 59.681,296.31918 V 77.180801"
id="path5311" />
<path
id="path5313"
d="M 69.681,296.31918 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 79.681,296.31918 V 77.180801"
id="path5315" />
<path
id="path5317"
d="M 89.681,296.31918 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 99.681,296.31918 V 77.180801"
id="path5319" />
<path
id="path5321"
d="M 109.681,296.31918 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 119.681,296.31918 V 77.180801"
id="path5323" />
<path
id="path5325"
d="M 129.681,296.31918 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 139.681,296.31918 V 77.180801"
id="path5327" />
<path
id="path5329"
d="M 149.681,296.31918 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 159.681,296.31918 V 77.180801"
id="path5331" />
<path
id="path5333"
d="m 169.681,296.31918 v -3.0895 m 0,-10.12031 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 179.681,296.31918 v -2.82492 m 0,-10.18645 v -6.87917 m 0,-28.50885 V 77.180801"
id="path5335"
sodipodi:nodetypes="cccccc" />
<path
id="path5337"
d="m 189.681,296.31918 v -2.95721 m 0,-50.37005 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 199.681,296.31918 v -3.15564 m 0,-9.72344 v -4.69636 m 0,-30.8901 V 77.180801"
id="path5339"
sodipodi:nodetypes="cccccc" />
<path
id="path5341"
d="m 209.681,296.31918 v -2.89106 m 0,-10.25261 v -14.61822 m 0,-12.76615 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
id="path5343"
d="m 219.81921,287.319 h -3.39004 m -51.52761,0 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 219.81921,277.319 h -18.00827 m -21.36511,0 H 0.68079633"
id="path5345"
sodipodi:nodetypes="cccc" />
<path
id="path5347"
d="m 219.81921,267.319 h -8.94629 m -36.90938,0 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 219.81921,257.319 h -8.74786 m -36.90937,0 H 0.68079633"
id="path5349"
sodipodi:nodetypes="cccc" />
<path
id="path5351"
d="m 219.81921,247.319 h -21.44786 m -18.12395,0 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 219.81921,237.319 H 0.68079633"
id="path5353" />
<path
id="path5355"
d="M 219.81921,227.319 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 219.81921,217.319 H 0.68079633"
id="path5357" />
<path
id="path5359"
d="M 219.81921,207.319 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 219.81921,197.319 H 0.68079633"
id="path5361" />
<path
id="path5363"
d="M 219.81921,187.319 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 219.81921,177.319 H 0.68079633"
id="path5365" />
<path
id="path5367"
d="M 219.81921,167.319 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 219.81921,157.319 H 0.68079633"
id="path5369" />
<path
id="path5371"
d="M 219.81921,147.319 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 219.81921,137.319 H 0.68079633"
id="path5373" />
<path
id="path5375"
d="M 219.81921,127.319 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 219.81921,117.319 H 0.68079633"
id="path5377" />
<path
id="path5379"
d="M 219.81921,107.319 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 219.81921,97.319 H 0.68079633"
id="path5381" />
<path
id="path5383"
d="M 219.81921,87.319 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
</g>
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Layer 3"
style="display:inline">
<path
style="fill:none;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0.25,205.25001 h 152.88208 m 53.78799,0 h 3.32996"
id="path973"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 145.25002,210.25 V 0.25000128"
id="path971"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 205.25001,210.25 v -2.79018 m 0,-10.89792 V 0.25000128"
id="path969"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,15.250006 H 210.25003"
id="path967"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,155.25001 H 210.25003"
id="path965"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 55.249995,210.25 V 0.25000128"
id="path963"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0.25,175.25 h 160.97366 m 43.87229,0 h 5.15408"
id="path961"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 125.25003,210.25 V 0.25000128"
id="path959"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,35.250001 H 210.25003"
id="path957"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 195.25003,210.25 v -3.02405 m 0,-10.68744 0,-10.52373 m 0,-19.79114 0,-165.97363872"
id="path955"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 35.25,210.25 V 0.25000128"
id="path953"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 5.2500251,210.25 V 0.25000128"
id="path951"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,25.249986 H 210.25003"
id="path949"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,105.25 H 210.25003"
id="path947"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 105.25004,210.25 V 0.25000128"
id="path945"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,55.249996 H 210.25003"
id="path943"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 185.25001,210.25 v -3.02405 m 0,-10.73421 v -2.8531 m 0,-34.30738 V 0.25000128"
id="path941"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 175.25,210.25 v -3.11759 m 0,-51.39092 V 0.25000128"
id="path939"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 15.250005,210.25 V 0.25000128"
id="path937"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,125.25 H 210.25003"
id="path935"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 85.250005,210.25 V 0.25000128"
id="path933"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,75.249991 H 210.25003"
id="path931"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 155.25,210.25 v -3.02405 m 0,-7.39 V 0.25000128"
id="path929"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,0.25000128 V 210.25"
id="path927"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,5.2499912 H 210.25003"
id="path925"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,145.24999 H 210.25003"
id="path923"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 65.25001,210.25 V 0.25000128"
id="path921"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,95.250021 H 210.25003"
id="path919"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 165.25002,210.25 v -2.79018 m 0,-10.4302 v -13.18975 m 0,-14.82678 V 0.25000128"
id="path917"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 135.25001,210.25 V 0.25000128"
id="path915"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 210.25003,0.25000128 H 0.25"
id="path913"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0.25,165.25002 h 166.9605 m 26.98754,0 h 16.05199"
id="path911"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 45.250015,210.25 V 0.25000128"
id="path909"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 115.25001,210.25 V 0.25000128"
id="path907"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,45.249981 H 210.25003"
id="path905"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 210.25003,210.25 V 0.25000128"
id="path903"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 25.25002,210.25 V 0.25000128"
id="path901"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0.25,185.25002 h 166.25892 m 30.26159,0 h 13.47952"
id="path899"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,115.25002 H 210.25003"
id="path897"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 95.25002,210.25 V 0.25000128"
id="path895"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,65.250011 H 210.25003"
id="path893"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,210.25 H 210.25003"
id="path891"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,85.250006 H 210.25003"
id="path889"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0.25,195.25 h 172.9694 m 6.46576,0 h 30.56487"
id="path887"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,135.25001 H 210.25003"
id="path885"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 75.250025,210.25 V 0.25000128"
id="path875"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.
+1
View File
@@ -0,0 +1 @@
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 623.62 623.62"><defs><style>.cls-1{fill:#9b9b9b;}</style></defs><rect class="cls-1" x="88.68" width="2.83" height="623.62"/><rect class="cls-1" x="177.37" width="2.83" height="623.62"/><rect class="cls-1" x="266.05" width="2.83" height="623.62"/><rect class="cls-1" x="354.74" width="2.83" height="623.62"/><rect class="cls-1" y="532.1" width="623.62" height="2.83"/><rect class="cls-1" y="443.42" width="623.62" height="2.83"/><rect class="cls-1" y="354.74" width="623.62" height="2.83"/><rect class="cls-1" y="266.05" width="623.62" height="2.83"/><rect class="cls-1" y="177.37" width="623.62" height="2.83"/><rect class="cls-1" y="88.68" width="623.62" height="2.83"/><polygon class="cls-1" points="463.4 569.23 456.58 569.23 463.52 579.05 466.99 574.34 463.4 569.23"/><polygon class="cls-1" points="449.04 583.69 431.76 568.39 431.76 575.78 454.61 595.96 454.61 569.23 449.04 569.23 449.04 583.69"/><polygon class="cls-1" points="474.72 569.23 465.49 581.86 465.49 595.13 471.08 595.13 471.08 583.67 481.62 569.23 474.72 569.23"/><polygon class="cls-1" points="411.59 574.08 417.44 585.36 415.25 585.36 412.47 590.8 420.2 590.8 422.46 595.13 428.7 595.13 414.75 568.04 411.59 574.08"/><polygon class="cls-1" points="431.76 595.13 431.76 595.14 437.33 595.14 437.33 584.78 431.76 579.89 431.76 595.13"/><polygon class="cls-1" points="409.91 577.43 400.81 595.13 407.03 595.13 413.01 583.51 409.91 577.43"/><path class="cls-1" d="M553.85,584.56a7.09,7.09,0,0,0,2.47-1.16,7.53,7.53,0,0,0,3.1-6.4,7.29,7.29,0,0,0-3.08-6.17,7.5,7.5,0,0,0-2.61-1.25,16.47,16.47,0,0,0-3.9-.35h-1.4v5.5h2a5.48,5.48,0,0,1,2.53.4,2,2,0,0,1,1,1.89,2.15,2.15,0,0,1-1.26,2.11,6.35,6.35,0,0,1-2.48.35h-1.79v5.44h2A14,14,0,0,0,553.85,584.56Z"/><path class="cls-1" d="M484.23,573.91a13.26,13.26,0,0,0-2.83,8.32A13.6,13.6,0,0,0,486.72,593a14.31,14.31,0,0,0,6.86,2.83v-5.62a8.32,8.32,0,0,1-5.12-3.41,8.06,8.06,0,0,1-.72-7.94,7.91,7.91,0,0,1,2-2.77,8.72,8.72,0,0,1,3.86-1.91v-5.63A13.78,13.78,0,0,0,484.23,573.91Z"/><path class="cls-1" d="M501.61,577.34l4.36-3.47a13,13,0,0,0-4-3.57,14.16,14.16,0,0,0-5.26-1.71v5.63A8,8,0,0,1,501.61,577.34Z"/><path class="cls-1" d="M597.1,577.34l4.37-3.47a13.16,13.16,0,0,0-4-3.57,14.16,14.16,0,0,0-5.26-1.71v5.63A8,8,0,0,1,597.1,577.34Z"/><path class="cls-1" d="M554,586.65a2.51,2.51,0,0,1,.06.55,2.1,2.1,0,0,1-1.46,2.2,7.69,7.69,0,0,1-2.34.27h-1.79v5.44h1.83a16.47,16.47,0,0,0,3.57-.32,6.93,6.93,0,0,0,2.45-1.09,7.45,7.45,0,0,0,2.38-2.76,7.91,7.91,0,0,0,.88-3.63,8.29,8.29,0,0,0-.68-3.37A7.94,7.94,0,0,1,554,586.65Z"/><path class="cls-1" d="M579.72,573.91a13.19,13.19,0,0,0-2.82,8.32A13.58,13.58,0,0,0,582.22,593a14.23,14.23,0,0,0,6.86,2.83v-5.62a8.34,8.34,0,0,1-5.13-3.41,8.09,8.09,0,0,1-.72-7.94,7.82,7.82,0,0,1,2-2.77,8.67,8.67,0,0,1,3.86-1.91v-5.63A13.78,13.78,0,0,0,579.72,573.91Z"/><path class="cls-1" d="M496.68,590.22v5.63a13.52,13.52,0,0,0,9.35-5.3l-4.4-3.41A8.15,8.15,0,0,1,496.68,590.22Z"/><path class="cls-1" d="M597.12,587.14a8.12,8.12,0,0,1-5,3.08v5.63a13.57,13.57,0,0,0,9.36-5.3Z"/><rect class="cls-1" x="565.38" y="569.23" width="5.57" height="25.9"/><path class="cls-1" d="M517.88,587.8a9.64,9.64,0,0,1-.72-4.31V569.23h-5.59V584a14.56,14.56,0,0,0,.62,4.68,9.52,9.52,0,0,0,2.06,3.46,10.53,10.53,0,0,0,2.91,2.28,11.21,11.21,0,0,0,3.51,1.23v-5.69A5.06,5.06,0,0,1,517.88,587.8Z"/><path class="cls-1" d="M527.31,583.51a9.5,9.5,0,0,1-.76,4.37,4.91,4.91,0,0,1-2.81,2.08v5.69a11,11,0,0,0,3.53-1.21,11.42,11.42,0,0,0,2.92-2.3,9.4,9.4,0,0,0,2.07-3.46,15.14,15.14,0,0,0,.62-4.68V569.23h-5.57Z"/><rect class="cls-1" x="539.74" y="569.23" width="5.57" height="25.9"/><rect class="cls-1" x="532.1" width="2.83" height="555.5"/><rect class="cls-1" x="532.1" y="608.78" width="2.83" height="14.84"/><rect class="cls-1" x="443.42" width="2.83" height="555.5"/><rect class="cls-1" x="443.42" y="608.78" width="2.83" height="14.84"/></svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.
+42
View File
@@ -0,0 +1,42 @@
min_slic3r_version = 1.2.2
1.2.2 Optimize parameters
min_slic3r_version = 1.2.1
1.2.1 Optimize parameters
min_slic3r_version = 1.2.0
1.2.0 Optimize parameters
min_slic3r_version = 1.1.7
1.1.7 Optimize parameters
min_slic3r_version = 1.1.6
1.1.6 Optimize parameters
min_slic3r_version = 1.1.5
1.1.5 Optimize parameters
min_slic3r_version = 1.1.4
1.1.4 Optimize parameters
min_slic3r_version = 1.1.3
1.1.3 Optimize parameters
min_slic3r_version = 1.1.2
1.1.2 Optimize parameters
min_slic3r_version = 1.1.1
1.1.1 Optimize parameters
min_slic3r_version = 1.1.0
1.1.0 Optimize parameters
min_slic3r_version = 1.0.9
1.0.9 Optimize parameters
min_slic3r_version = 1.0.8
1.0.8 Optimize parameters
min_slic3r_version = 1.0.7
1.0.7 Optimize parameters
min_slic3r_version = 1.0.6
1.0.6 Optimize parameters
min_slic3r_version = 1.0.5
1.0.5 Optimize parameters
min_slic3r_version = 1.0.4
1.0.4 Modify start code
min_slic3r_version = 1.0.3
1.0.3 Delete filament property
min_slic3r_version = 1.0.2
1.0.2 Add filament property
min_slic3r_version = 1.0.1
1.0.1 Optimize parameters
min_slic3r_version = 1.0.0
1.0.0 Initial
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="694.5px" height="694.5px" viewBox="0 0 694.5 694.5" enable-background="new 0 0 694.5 694.5" xml:space="preserve">
<rect fill="#FFFFFF" width="1.4" height="694.5"/>
<rect x="693.1" y="15.7" fill="#FFFFFF" width="1.4" height="678.8"/>
<rect x="0" y="693.1" fill="#FFFFFF" width="694.5" height="1.4"/>
<rect x="559.1" fill="#FFFFFF" width="1.4" height="694.5"/>
<rect x="417.4" fill="#FFFFFF" width="1.4" height="694.5"/>
<rect x="275.7" y="15.7" fill="#FFFFFF" width="1.4" height="678.8"/>
<rect x="133.9" y="14.2" fill="#FFFFFF" width="1.4" height="680.3"/>
<rect x="21.1" y="0" fill="#FFFFFF" width="0.4" height="694.5"/>
<rect x="49.4" y="0" fill="#FFFFFF" width="0.4" height="694.5"/>
<rect x="77.8" y="14.2" fill="#FFFFFF" width="0.4" height="680.3"/>
<rect x="106.1" y="14.2" fill="#FFFFFF" width="0.4" height="680.3"/>
<rect x="162.8" y="15.7" fill="#FFFFFF" width="0.4" height="678.8"/>
<rect x="191.1" y="15.7" fill="#FFFFFF" width="0.4" height="678.8"/>
<rect x="219.5" y="15.7" fill="#FFFFFF" width="0.4" height="678.8"/>
<rect x="247.8" y="15.7" fill="#FFFFFF" width="0.4" height="678.8"/>
<rect x="304.5" y="15.7" fill="#FFFFFF" width="0.4" height="678.8"/>
<rect x="332.9" y="0" fill="#FFFFFF" width="0.4" height="694.5"/>
<rect x="361.2" y="0" fill="#FFFFFF" width="0.4" height="694.5"/>
<rect x="389.6" y="0" fill="#FFFFFF" width="0.4" height="694.5"/>
<rect x="446.3" y="0" fill="#FFFFFF" width="0.4" height="694.5"/>
<rect x="474.6" y="0" fill="#FFFFFF" width="0.4" height="694.5"/>
<rect x="502.9" y="0" fill="#FFFFFF" width="0.4" height="694.5"/>
<rect x="531.3" y="0" fill="#FFFFFF" width="0.4" height="694.5"/>
<rect x="588" y="0" fill="#FFFFFF" width="0.4" height="694.5"/>
<rect x="616.3" y="15.7" fill="#FFFFFF" width="0.4" height="678.8"/>
<rect x="644.7" y="15.7" fill="#FFFFFF" width="0.4" height="678.8"/>
<rect x="673" y="15.7" fill="#FFFFFF" width="0.4" height="678.8"/>
<rect x="0" y="559.1" fill="#FFFFFF" width="694.5" height="1.4"/>
<rect x="0" y="417.4" fill="#FFFFFF" width="694.5" height="1.4"/>
<rect x="0" y="275.7" fill="#FFFFFF" width="694.5" height="1.4"/>
<rect x="0" y="133.9" fill="#FFFFFF" width="694.5" height="1.4"/>
<rect x="0" y="21.1" fill="#FFFFFF" width="694.5" height="0.4"/>
<rect x="0" y="49.4" fill="#FFFFFF" width="694.5" height="0.4"/>
<rect x="0" y="77.8" fill="#FFFFFF" width="694.5" height="0.4"/>
<rect x="0" y="106.1" fill="#FFFFFF" width="694.5" height="0.4"/>
<rect x="0" y="162.8" fill="#FFFFFF" width="694.5" height="0.4"/>
<rect x="0" y="191.1" fill="#FFFFFF" width="694.5" height="0.4"/>
<rect x="0" y="219.5" fill="#FFFFFF" width="694.5" height="0.4"/>
<rect x="0" y="247.8" fill="#FFFFFF" width="694.5" height="0.4"/>
<rect x="0" y="304.5" fill="#FFFFFF" width="694.5" height="0.4"/>
<rect x="0" y="332.9" fill="#FFFFFF" width="694.5" height="0.4"/>
<rect x="0" y="361.2" fill="#FFFFFF" width="694.5" height="0.4"/>
<rect x="0" y="389.6" fill="#FFFFFF" width="694.5" height="0.4"/>
<rect x="0" y="446.3" fill="#FFFFFF" width="694.5" height="0.4"/>
<rect x="0" y="474.6" fill="#FFFFFF" width="694.5" height="0.4"/>
<rect x="0" y="502.9" fill="#FFFFFF" width="694.5" height="0.4"/>
<rect x="0" y="531.3" fill="#FFFFFF" width="694.5" height="0.4"/>
<rect x="0" y="588" fill="#FFFFFF" width="694.5" height="0.4"/>
<rect x="0" y="616.3" fill="#FFFFFF" width="694.5" height="0.4"/>
<rect x="0" y="644.7" fill="#FFFFFF" width="694.5" height="0.4"/>
<rect x="0" y="673" fill="#FFFFFF" width="694.5" height="0.4"/>
<polygon fill="#FFFFFF" points="694.5,15.7 588.1,15.7 588.1,0 589.6,0 589.6,14 694.5,14 "/>
<polygon fill="#FFFFFF" points="327.5,15.7 69.4,15.7 69.4,0 70.9,0 70.9,14 326,14 326,0 327.5,0 "/>
<rect x="327.5" y="0" fill="#FFFFFF" width="260.9" height="1.4"/>
<rect x="0" y="0" fill="#FFFFFF" width="69.4" height="1.4"/>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 921.3 921.3" style="enable-background:new 0 0 921.3 921.3;" xml:space="preserve">
<rect y="899.8" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="871.5" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="843.1" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="814.3" style="fill:#FFFFFF;" width="921.3" height="1.4"/>
<rect y="919.9" style="fill:#FFFFFF;" width="921.3" height="1.4"/>
<rect y="786.4" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="758.1" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="729.7" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="701.4" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="672.5" style="fill:#FFFFFF;" width="921.3" height="1.4"/>
<rect y="644.7" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="616.3" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="588" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="559.6" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="530.8" style="fill:#FFFFFF;" width="921.3" height="1.4"/>
<rect y="502.9" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="474.6" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="446.3" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="417.9" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="389.1" style="fill:#FFFFFF;" width="921.3" height="1.4"/>
<rect y="361.2" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="332.9" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="304.5" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="276.2" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="247.3" style="fill:#FFFFFF;" width="921.3" height="1.4"/>
<rect y="219.5" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="191.1" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="162.8" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="134.4" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="105.6" style="fill:#FFFFFF;" width="921.3" height="1.4"/>
<rect y="0" style="fill:#FFFFFF;" width="921.3" height="1.4"/>
<rect y="77.8" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="49.4" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect y="21.1" style="fill:#FFFFFF;" width="921.3" height="0.4"/>
<rect x="21.1" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="49.4" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="77.8" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="105.6" y="0" style="fill:#FFFFFF;" width="1.4" height="921.3"/>
<rect y="0" style="fill:#FFFFFF;" width="1.4" height="921.3"/>
<rect x="134.4" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="162.8" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="191.1" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="219.5" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="247.3" y="0" style="fill:#FFFFFF;" width="1.4" height="921.3"/>
<rect x="276.2" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="304.5" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="332.9" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="361.2" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="389.1" y="0" style="fill:#FFFFFF;" width="1.4" height="921.3"/>
<rect x="417.9" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="446.3" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="474.6" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="502.9" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="530.8" y="0" style="fill:#FFFFFF;" width="1.4" height="921.3"/>
<rect x="559.6" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="588" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="616.3" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="644.7" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="672.5" y="0" style="fill:#FFFFFF;" width="1.4" height="921.3"/>
<rect x="701.4" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="729.7" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="758.1" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="786.4" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="814.3" y="0" style="fill:#FFFFFF;" width="1.4" height="921.3"/>
<rect x="919.9" y="0" style="fill:#FFFFFF;" width="1.4" height="921.3"/>
<rect x="843.1" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="871.5" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
<rect x="899.8" y="0" style="fill:#FFFFFF;" width="0.4" height="921.3"/>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 793.7 793.7" style="enable-background:new 0 0 793.7 793.7;" xml:space="preserve">
<rect y="14" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect x="14" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="41.8" y="0" style="fill:#FFFFFF;" width="1.4" height="793.7"/>
<rect x="70.7" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="99" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="127.4" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="155.7" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="183.6" y="0" style="fill:#FFFFFF;" width="1.4" height="793.7"/>
<rect x="212.4" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="240.7" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="269.1" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="297.4" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="325.3" y="0" style="fill:#FFFFFF;" width="1.4" height="793.7"/>
<rect x="354.1" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="382.5" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="410.8" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="439.2" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="467" y="0" style="fill:#FFFFFF;" width="1.4" height="793.7"/>
<rect x="495.9" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="524.2" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="552.6" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="580.9" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="608.7" y="0" style="fill:#FFFFFF;" width="1.4" height="793.7"/>
<rect x="637.6" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="665.9" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="694.3" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="722.6" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect x="750.5" y="0" style="fill:#FFFFFF;" width="1.4" height="793.7"/>
<rect x="792.3" y="0" style="fill:#FFFFFF;" width="1.4" height="793.7"/>
<rect y="0" style="fill:#FFFFFF;" width="1.4" height="793.7"/>
<rect x="779.3" y="0" style="fill:#FFFFFF;" width="0.4" height="793.7"/>
<rect y="41.8" style="fill:#FFFFFF;" width="793.7" height="1.4"/>
<rect y="0" style="fill:#FFFFFF;" width="793.7" height="1.4"/>
<rect y="792.3" style="fill:#FFFFFF;" width="793.7" height="1.4"/>
<rect y="70.7" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect y="99" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect y="127.4" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect y="155.7" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect y="183.6" style="fill:#FFFFFF;" width="793.7" height="1.4"/>
<rect y="212.4" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect y="240.7" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect y="269.1" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect y="297.4" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect y="325.3" style="fill:#FFFFFF;" width="793.7" height="1.4"/>
<rect y="354.1" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect y="382.5" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect y="410.8" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect y="439.2" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect y="467" style="fill:#FFFFFF;" width="793.7" height="1.4"/>
<rect y="495.9" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect y="524.2" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect y="552.6" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect y="580.9" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect y="608.7" style="fill:#FFFFFF;" width="793.7" height="1.4"/>
<rect y="637.6" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect y="665.9" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect y="694.3" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect y="722.6" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
<rect y="750.5" style="fill:#FFFFFF;" width="793.7" height="1.4"/>
<rect y="779.3" style="fill:#FFFFFF;" width="793.7" height="0.4"/>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1_x5F_复制" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px" y="0px" viewBox="0 0 864.6 864.6" enable-background="new 0 0 864.6 864.6" xml:space="preserve">
<rect x="0" y="843.1" fill="#FFFFFF" width="830.6" height="0.4"/>
<rect x="0.4" y="814.8" fill="#FFFFFF" width="829.5" height="0.4"/>
<rect x="0" y="785.9" fill="#FFFFFF" width="863.8" height="1.4"/>
<rect x="0" y="758.1" fill="#FFFFFF" width="863.8" height="0.4"/>
<rect x="0" y="729.7" fill="#FFFFFF" width="863.8" height="0.4"/>
<rect x="0" y="701.4" fill="#FFFFFF" width="863.8" height="0.4"/>
<rect x="0" y="673" fill="#FFFFFF" width="863.8" height="0.4"/>
<rect x="0" y="644.2" fill="#FFFFFF" width="863.8" height="1.4"/>
<rect x="0" y="616.3" fill="#FFFFFF" width="863.8" height="0.4"/>
<rect x="0" y="588" fill="#FFFFFF" width="863.8" height="0.4"/>
<rect x="0" y="559.6" fill="#FFFFFF" width="863.8" height="0.4"/>
<rect x="0" y="531.3" fill="#FFFFFF" width="863.8" height="0.4"/>
<rect x="0" y="502.4" fill="#FFFFFF" width="863.8" height="1.4"/>
<rect x="0.1" y="474.6" fill="#FFFFFF" width="863.7" height="0.4"/>
<rect x="0" y="446.3" fill="#FFFFFF" width="863.8" height="0.4"/>
<rect x="0.1" y="417.9" fill="#FFFFFF" width="863.7" height="0.4"/>
<rect x="0" y="389.6" fill="#FFFFFF" width="863.8" height="0.4"/>
<rect x="0" y="360.7" fill="#FFFFFF" width="863.8" height="1.4"/>
<rect x="0" y="332.9" fill="#FFFFFF" width="863.8" height="0.4"/>
<rect x="0" y="304.5" fill="#FFFFFF" width="863.8" height="0.4"/>
<rect x="0" y="276.2" fill="#FFFFFF" width="863.8" height="0.4"/>
<rect x="0" y="247.8" fill="#FFFFFF" width="863.8" height="0.4"/>
<rect x="0" y="219" fill="#FFFFFF" width="863.8" height="1.4"/>
<rect x="0" y="191.1" fill="#FFFFFF" width="863.8" height="0.4"/>
<rect x="0" y="162.8" fill="#FFFFFF" width="863.8" height="0.4"/>
<rect x="0" y="134.4" fill="#FFFFFF" width="863.8" height="0.4"/>
<rect x="0" y="106.1" fill="#FFFFFF" width="863.8" height="0.4"/>
<rect x="0" y="77.3" fill="#FFFFFF" width="863.8" height="1.4"/>
<rect y="863.2" fill="#FFFFFF" width="830.6" height="1.4"/>
<rect x="100.6" fill="#FFFFFF" width="764" height="1.4"/>
<rect y="8.5" fill="#FFFFFF" width="100.6" height="1.4"/>
<rect x="829.2" y="806.5" fill="#FFFFFF" width="35.4" height="1.4"/>
<rect x="829.2" y="807.9" fill="#FFFFFF" width="1.4" height="56.7"/>
<rect x="99.2" y="0" fill="#FFFFFF" width="1.4" height="9.2"/>
<rect x="0" y="8.9" fill="#FFFFFF" width="1.4" height="855.7"/>
<rect x="863.2" fill="#FFFFFF" width="1.4" height="807.2"/>
<rect x="0" y="49.4" fill="#FFFFFF" width="863.8" height="0.4"/>
<rect x="0" y="21.1" fill="#FFFFFF" width="864.6" height="0.4"/>
<rect x="21.1" y="9.6" fill="#FFFFFF" width="0.4" height="855"/>
<rect x="49.4" y="9.6" fill="#FFFFFF" width="0.4" height="855"/>
<rect x="77.3" y="9.6" fill="#FFFFFF" width="1.4" height="855"/>
<rect x="106.1" y="0.7" fill="#FFFFFF" width="0.4" height="863.9"/>
<rect x="134.4" y="0.7" fill="#FFFFFF" width="0.4" height="863.9"/>
<rect x="162.8" y="0" fill="#FFFFFF" width="0.4" height="864.6"/>
<rect x="191.1" y="0" fill="#FFFFFF" width="0.4" height="864.6"/>
<rect x="219" y="0" fill="#FFFFFF" width="1.4" height="864.6"/>
<rect x="247.8" y="0" fill="#FFFFFF" width="0.4" height="864.6"/>
<rect x="276.2" y="0" fill="#FFFFFF" width="0.4" height="864.6"/>
<rect x="304.5" y="0" fill="#FFFFFF" width="0.4" height="864.6"/>
<rect x="332.9" y="0" fill="#FFFFFF" width="0.4" height="864.6"/>
<rect x="360.7" y="0" fill="#FFFFFF" width="1.4" height="864.6"/>
<rect x="389.6" y="0" fill="#FFFFFF" width="0.4" height="864.6"/>
<rect x="417.9" y="0" fill="#FFFFFF" width="0.4" height="864.6"/>
<rect x="446.3" y="0" fill="#FFFFFF" width="0.4" height="864.6"/>
<rect x="474.6" y="0" fill="#FFFFFF" width="0.4" height="864.6"/>
<rect x="502.4" y="0" fill="#FFFFFF" width="1.4" height="864.6"/>
<rect x="531.3" y="0" fill="#FFFFFF" width="0.4" height="864.6"/>
<rect x="559.6" y="0" fill="#FFFFFF" width="0.4" height="864.6"/>
<rect x="588" y="0" fill="#FFFFFF" width="0.4" height="864.6"/>
<rect x="616.3" y="0" fill="#FFFFFF" width="0.4" height="864.6"/>
<rect x="644.2" y="0" fill="#FFFFFF" width="1.4" height="864.6"/>
<rect x="673" y="0" fill="#FFFFFF" width="0.4" height="864.6"/>
<rect x="701.4" y="0" fill="#FFFFFF" width="0.4" height="864.6"/>
<rect x="729.7" y="0" fill="#FFFFFF" width="0.4" height="864.6"/>
<rect x="758.1" y="0" fill="#FFFFFF" width="0.4" height="864.6"/>
<rect x="785.9" y="0" fill="#FFFFFF" width="1.4" height="864.6"/>
<rect x="814.8" y="0" fill="#FFFFFF" width="0.4" height="864.6"/>
<rect x="843.1" y="0" fill="#FFFFFF" width="0.4" height="807.2"/>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 496.1 510.2" style="enable-background:new 0 0 496.1 510.2;" xml:space="preserve">
<rect x="0" y="14" style="fill:#FFFFFF;" width="496.1" height="0.4"/>
<rect x="0" y="41.8" style="fill:#FFFFFF;" width="496.1" height="1.4"/>
<rect x="0" style="fill:#FFFFFF;" width="496.1" height="1.4"/>
<rect x="0" y="508.8" style="fill:#FFFFFF;" width="496.1" height="1.4"/>
<rect x="0" y="70.7" style="fill:#FFFFFF;" width="496.1" height="0.4"/>
<rect x="0" y="99" style="fill:#FFFFFF;" width="496.1" height="0.4"/>
<rect x="0" y="127.4" style="fill:#FFFFFF;" width="496.1" height="0.4"/>
<rect x="0" y="155.7" style="fill:#FFFFFF;" width="496.1" height="0.4"/>
<rect x="0" y="183.6" style="fill:#FFFFFF;" width="496.1" height="1.4"/>
<rect x="0" y="212.4" style="fill:#FFFFFF;" width="496.1" height="0.4"/>
<rect x="0" y="240.7" style="fill:#FFFFFF;" width="496.1" height="0.4"/>
<rect x="0" y="269.1" style="fill:#FFFFFF;" width="496.1" height="0.4"/>
<rect x="0" y="297.4" style="fill:#FFFFFF;" width="496.1" height="0.4"/>
<rect x="0" y="325.3" style="fill:#FFFFFF;" width="496.1" height="1.4"/>
<rect x="0" y="354.1" style="fill:#FFFFFF;" width="496.1" height="0.4"/>
<rect x="0" y="382.5" style="fill:#FFFFFF;" width="496.1" height="0.4"/>
<rect x="0" y="410.8" style="fill:#FFFFFF;" width="496.1" height="0.4"/>
<rect x="0" y="439.2" style="fill:#FFFFFF;" width="496.1" height="0.4"/>
<rect x="0" y="467" style="fill:#FFFFFF;" width="496.1" height="1.4"/>
<rect x="0" y="495.9" style="fill:#FFFFFF;" width="496.1" height="0.4"/>
<rect x="6.9" y="0" style="fill:#FFFFFF;" width="0.4" height="510.2"/>
<rect x="34.7" y="0" style="fill:#FFFFFF;" width="1.4" height="510.2"/>
<rect x="63.6" y="0" style="fill:#FFFFFF;" width="0.4" height="510.2"/>
<rect x="91.9" y="0" style="fill:#FFFFFF;" width="0.4" height="510.2"/>
<rect x="120.3" y="0" style="fill:#FFFFFF;" width="0.4" height="510.2"/>
<rect x="148.6" y="0" style="fill:#FFFFFF;" width="0.4" height="510.2"/>
<rect x="176.5" y="0" style="fill:#FFFFFF;" width="1.4" height="510.2"/>
<rect x="205.3" y="0" style="fill:#FFFFFF;" width="0.4" height="510.2"/>
<rect x="233.7" y="0" style="fill:#FFFFFF;" width="0.4" height="510.2"/>
<rect x="262" y="0" style="fill:#FFFFFF;" width="0.4" height="510.2"/>
<rect x="290.4" y="0" style="fill:#FFFFFF;" width="0.4" height="510.2"/>
<rect x="318.2" y="0" style="fill:#FFFFFF;" width="1.4" height="510.2"/>
<rect x="347" y="0" style="fill:#FFFFFF;" width="0.4" height="510.2"/>
<rect x="375.4" y="0" style="fill:#FFFFFF;" width="0.4" height="510.2"/>
<rect x="403.7" y="0" style="fill:#FFFFFF;" width="0.4" height="510.2"/>
<rect x="432.1" y="0" style="fill:#FFFFFF;" width="0.4" height="510.2"/>
<rect x="459.9" y="0" style="fill:#FFFFFF;" width="1.4" height="510.2"/>
<rect x="494.7" y="0" style="fill:#FFFFFF;" width="1.4" height="510.2"/>
<rect y="0" style="fill:#FFFFFF;" width="1.4" height="510.2"/>
<rect x="488.8" y="0" style="fill:#FFFFFF;" width="0.4" height="510.2"/>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

+10
View File
@@ -0,0 +1,10 @@
[
{
"name": "Slice Beam's Vendor Dumps",
"description": "Profiles from proprietary slicers",
"visibility": "",
"id": "beamdumps-fff",
"url": "https://raw.githubusercontent.com/utkabobr/SliceBeam/refs/heads/master/.profiledumpsrepo/",
"index_url": "https://raw.githubusercontent.com/utkabobr/SliceBeam/refs/heads/master/.profiledumpsrepo/vendor_indices.zip"
}
]
Binary file not shown.
+4 -2
View File
@@ -10,7 +10,9 @@ It is based on PrusaSlicer's core and well optimized for Android touchscreen int
- K3D Chat for discussion & support (Russian language only): https://t.me/K_3_D
# Quick Start
Just download APK from [Releases tab](https://github.com/utkabobr/SliceBeam/releases/latest) and follow setup instructions. Google Play builds will be available later.
[<img src="/.github/img/getongp.svg">](https://play.google.com/store/apps/details?id=ru.ytkab0bp.slicebeam)
Or download APK from [Releases tab](https://github.com/utkabobr/SliceBeam/releases/latest) and follow setup instructions.
# Where to get printer profiles?
@@ -66,4 +68,4 @@ Copy .so libs to app/src/main/occt/jniLibs; Include files to app/src/main/occt/i
You can also reduce size of libraries by removing unnecessary modules (Check app/src/main/occt/jniLibs/clean.py)
## Why not to include all of them in repo?
They're HUGE (More than 2 Gb) binary files, so you must build them yourself.
They're HUGE (More than 2 Gb) binary files, so you must build them yourself.
Submodule
+1
Submodule SAPIL added at d0c6422d79
+4
View File
@@ -22,6 +22,8 @@ add_compile_options(-fsigned-char)
# Suppress all warnings
add_definitions(-w)
add_definitions(-DNDEBUG)
add_definitions(-DSLIC3R_VERSION=${SLIC3R_VERSION})
add_definitions(-DSLIC3R_BUILD_ID=${SLIC3R_BUILD_ID})
set(jni_imports ${CMAKE_SOURCE_DIR}/src/main/jniImports)
set(jni_libs ${CMAKE_SOURCE_DIR}/src/main/jniLibs)
@@ -1237,6 +1239,8 @@ add_library(slic3r
src/main/jni/libnest2d/src/libnest2d.cpp
src/main/jni/bbl/Orient.cpp
src/main/jni/slicebeam/beam_native.cpp
src/main/jni/slicebeam/GLModel.cpp
src/main/jni/slicebeam/GLShader.cpp
+11 -6
View File
@@ -6,21 +6,23 @@ def commit = getGitCommitHash(file('.'))
android {
namespace 'ru.ytkab0bp.slicebeam'
compileSdk 34
compileSdk 35
defaultConfig {
applicationId "ru.ytkab0bp.slicebeam"
minSdk 21
targetSdk 34
versionCode 5
versionName "0.1.2"
targetSdk 35
versionCode 8
versionName "0.3.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=c++_shared', '-DANDROID_PLATFORM=21',
'-DCMAKE_BUILD_TYPE=Release' // Disabling this results in drastically degradation of slicing times on debug builds
'-DCMAKE_BUILD_TYPE=Release', // Disabling this results in drastically degradation of slicing times on debug builds
"-DSLIC3R_VERSION=\"${defaultConfig.versionName}\"",
"-DSLIC3R_BUILD_ID=\"${defaultConfig.versionCode}\""
}
}
}
@@ -88,11 +90,14 @@ dependencies {
implementation project(":eventbus")
implementation project(":eventbus_api")
annotationProcessor project(":eventbus_processor")
implementation project(":sapil")
implementation 'com.google.code.gson:gson:2.11.0'
implementation 'com.github.instacart:truetime-android:4.0.0.alpha'
implementation 'com.github.mrudultora:Colorpicker:1.2.0'
implementation 'com.github.bumptech.glide:glide:4.16.0'
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'com.google.android.material:material:1.12.0'
implementation 'com.loopj.android:android-async-http:1.4.11'
implementation 'androidx.activity:activity:1.9.1'
implementation 'androidx.activity:activity:1.10.1'
}
+11
View File
@@ -5,6 +5,15 @@
<uses-feature android:glEsVersion="0x00030000" android:required="true"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<queries>
<intent>
<action android:name="android.media.action.IMAGE_CAPTURE" />
</intent>
<!-- WebView fails sometime if not queried, idk why -->
<package android:name="com.google.android.webview"/>
</queries>
<application
android:allowBackup="true"
@@ -16,6 +25,8 @@
android:theme="@style/Theme.SliceBeam"
android:name=".SliceBeam"
android:usesCleartextTraffic="true"
android:largeHeap="true"
android:isGame="false"
tools:targetApi="31">
<activity
android:name=".MainActivity"
@@ -15,6 +15,7 @@ import java.util.List;
import java.util.Locale;
import cz.msebera.android.httpclient.Header;
import ru.ytkab0bp.slicebeam.cloud.CloudController;
import ru.ytkab0bp.slicebeam.events.BeamServerDataUpdatedEvent;
import ru.ytkab0bp.slicebeam.utils.Prefs;
@@ -45,6 +46,10 @@ public class BeamServerData {
return !BuildConfig.IS_GOOGLE_PLAY || Prefs.isRussianIP();
}
public static boolean isCloudAvailable() {
return isBoostyAvailable() && CloudController.hasAccountFeatures();
}
public static void load() {
client.get(DATA_URL, new AsyncHttpResponseHandler() {
@Override
@@ -2,20 +2,24 @@ package ru.ytkab0bp.slicebeam;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Process;
import android.provider.MediaStore;
import android.util.Base64;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.view.WindowManager;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -28,6 +32,7 @@ import org.json.JSONArray;
import org.json.JSONObject;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -40,27 +45,39 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.zip.ZipFile;
import ru.ytkab0bp.sapil.APICallback;
import ru.ytkab0bp.slicebeam.cloud.CloudAPI;
import ru.ytkab0bp.slicebeam.cloud.CloudController;
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.components.ChangeLogBottomSheet;
import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.events.NeedDismissAIGeneratorMenu;
import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.ObjectsListChangedEvent;
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import ru.ytkab0bp.slicebeam.navigation.Fragment;
import ru.ytkab0bp.slicebeam.navigation.MobileNavigationDelegate;
import ru.ytkab0bp.slicebeam.navigation.NavigationDelegate;
import ru.ytkab0bp.slicebeam.slic3r.Model;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rRuntimeError;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.IOUtils;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
public class MainActivity extends AppCompatActivity {
// Activity result
public final static int REQUEST_CODE_OPEN_FILE = 1, REQUEST_CODE_EXPORT_GCODE = 2,
REQUEST_CODE_IMPORT_PROFILES = 3, REQUEST_CODE_EXPORT_PROFILES = 4;
REQUEST_CODE_IMPORT_PROFILES = 3, REQUEST_CODE_EXPORT_PROFILES = 4,
REQUEST_CODE_EXPORT_3MF = 5,
REQUEST_CODE_AI_GENERATOR_TAKE_PHOTO = 6, REQUEST_CODE_AI_GENERATOR_CHOOSE_PHOTO = 7;
private static MainActivity activeInstance;
@@ -68,6 +85,10 @@ public class MainActivity extends AppCompatActivity {
public static List<ConfigObject> EXPORTING_FILAMENTS;
public static List<ConfigObject> EXPORTING_PRINTERS;
public static boolean IS_GENERATING_AI_MODEL;
public static File aiTempFile;
private static SparseArray<NavigationDelegate> liveDelegate = new SparseArray<>();
private static int lastId;
@@ -192,12 +213,39 @@ public class MainActivity extends AppCompatActivity {
setIntent(null);
}
/** @noinspection ResultOfMethodCallIgnored*/
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
if (requestCode == MainActivity.REQUEST_CODE_EXPORT_GCODE) {
if (requestCode == MainActivity.REQUEST_CODE_EXPORT_3MF) {
Fragment fragment = getNavigationDelegate().getCurrentFragment();
if (fragment instanceof BedFragment) {
try {
OutputStream out = getContentResolver().openOutputStream(data.getData());
Model model = ((BedFragment) fragment).getGlView().getRenderer().getModel();
File tempFile = File.createTempFile("temp_project", ".3mf");
SliceBeam.genCurrentConfig();
File cfg = SliceBeam.getCurrentConfigFile();
model.export3mf(cfg.getAbsolutePath(), tempFile.getAbsolutePath());
InputStream in = new FileInputStream(tempFile);
byte[] buffer = new byte[10240];
int c;
while ((c = in.read(buffer)) != -1) {
out.write(buffer, 0, c);
}
in.close();
out.close();
tempFile.delete();
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileExport3mfSuccess));
} catch (IOException | Slic3rRuntimeError e) {
throw new RuntimeException(e);
}
}
} else if (requestCode == MainActivity.REQUEST_CODE_EXPORT_GCODE) {
try {
OutputStream out = getContentResolver().openOutputStream(data.getData());
InputStream in = new FileInputStream(BedFragment.getTempGCodePath());
@@ -238,25 +286,14 @@ public class MainActivity extends AppCompatActivity {
OutputStream out = getContentResolver().openOutputStream(data.getData());
out.write(w.serialize().getBytes(StandardCharsets.UTF_8));
out.close();
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileExportProfilesSuccess));
} catch (IOException e) {
throw new RuntimeException(e);
}
} else if (requestCode == MainActivity.REQUEST_CODE_IMPORT_PROFILES) {
Uri uri = data.getData();
ContentResolver resolver = getContentResolver();
String[] projection = {MediaStore.MediaColumns.DISPLAY_NAME};
Cursor metaCursor = resolver.query(uri, projection, null, null, null);
String fileName = null;
if (metaCursor != null) {
try {
if (metaCursor.moveToFirst()) {
fileName = metaCursor.getString(0);
}
} finally {
metaCursor.close();
}
}
String fileName = IOUtils.getDisplayName(uri);
if (fileName == null) {
new BeamAlertDialogBuilder(this)
@@ -268,136 +305,7 @@ public class MainActivity extends AppCompatActivity {
}
if (fileName.endsWith(".orca_printer")) {
Toast.makeText(MainActivity.this, R.string.OrcaConversionPleaseWait, Toast.LENGTH_SHORT).show();
File f = new File(SliceBeam.getModelCacheDir(), "orca_conv.zip");
new Thread(()->{
try {
InputStream in = resolver.openInputStream(uri);
FileOutputStream fos = new FileOutputStream(f);
byte[] buffer = new byte[10240]; int c;
while ((c = in.read(buffer)) != -1) {
fos.write(buffer, 0, c);
}
fos.close();
in.close();
ZipFile zf = new ZipFile(f);
JSONObject bundle = new JSONObject(IOUtils.readString(zf.getInputStream(zf.getEntry("bundle_structure.json"))));
if (!bundle.get("bundle_type").equals("printer config bundle")) {
zf.close();
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileImportProfilesFailed)
.setMessage(R.string.OrcaConversionNotAConfigBundle)
.setPositiveButton(android.R.string.ok, null)
.show());
return;
}
Slic3rConfigWrapper w = new Slic3rConfigWrapper();
if (bundle.has("process_config")) {
JSONArray arr = bundle.getJSONArray("process_config");
List<String> names = new ArrayList<>();
List<String> stripped = new ArrayList<>();
for (int i = 0; i < arr.length(); i++) {
String v = arr.getString(i);
names.add(v);
stripped.add(v.substring(v.indexOf('/') + 1, v.length() - 5));
}
for (String name : names) {
w.printConfigs.add(IOUtils.configJsonToIni(new JSONObject(IOUtils.readString(zf.getInputStream(zf.getEntry(name)))), "process", Slic3rConfigWrapper.PRINT_CONFIG_KEYS, stripped));
}
for (ConfigObject obj : w.printConfigs) {
String inherit = obj.get("inherits");
while (inherit != null) {
ConfigObject _obj = w.findPrint(inherit);
if (_obj == null) throw new IOUtils.MissingProfileException(inherit);
obj.values.remove("inherits");
HashMap<String, String> newMap = new HashMap<>();
newMap.putAll(_obj.values);
newMap.putAll(obj.values);
obj.values = newMap;
inherit = obj.values.get("inherits");
}
}
}
if (bundle.has("filament_config")) {
JSONArray arr = bundle.getJSONArray("filament_config");
List<String> names = new ArrayList<>();
List<String> stripped = new ArrayList<>();
for (int i = 0; i < arr.length(); i++) {
String v = arr.getString(i);
names.add(v);
stripped.add(v.substring(v.indexOf('/') + 1, v.length() - 5));
}
for (String name : names) {
w.filamentConfigs.add(IOUtils.configJsonToIni(new JSONObject(IOUtils.readString(zf.getInputStream(zf.getEntry(name)))), "filament", Slic3rConfigWrapper.FILAMENT_CONFIG_KEYS, stripped));
}
for (ConfigObject obj : w.filamentConfigs) {
String inherit = obj.get("inherits");
while (inherit != null) {
ConfigObject _obj = w.findFilament(inherit);
if (_obj == null) throw new IOUtils.MissingProfileException(inherit);
obj.values.remove("inherits");
HashMap<String, String> newMap = new HashMap<>();
newMap.putAll(_obj.values);
newMap.putAll(obj.values);
obj.values = newMap;
inherit = obj.values.get("inherits");
}
}
}
if (bundle.has("printer_config")) {
JSONArray arr = bundle.getJSONArray("printer_config");
List<String> names = new ArrayList<>();
List<String> stripped = new ArrayList<>();
for (int i = 0; i < arr.length(); i++) {
String v = arr.getString(i);
names.add(v);
stripped.add(v.substring(v.indexOf('/') + 1));
}
for (String name : names) {
w.printerConfigs.add(IOUtils.configJsonToIni(new JSONObject(IOUtils.readString(zf.getInputStream(zf.getEntry(name)))), "machine", Slic3rConfigWrapper.PRINTER_CONFIG_KEYS, stripped));
}
for (ConfigObject obj : w.printerConfigs) {
String inherit = obj.get("inherits");
while (inherit != null) {
ConfigObject _obj = w.findPrinter(inherit);
if (_obj == null) throw new IOUtils.MissingProfileException(inherit);
obj.values.remove("inherits");
HashMap<String, String> newMap = new HashMap<>();
newMap.putAll(_obj.values);
newMap.putAll(obj.values);
obj.values = newMap;
inherit = obj.values.get("inherits");
}
}
}
zf.close();
loadIniForImport(new ByteArrayInputStream(w.serialize().getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
ViewUtils.postOnMainThread(() -> {
new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileImportProfilesFailed)
.setMessage(e.toString())
.setPositiveButton(android.R.string.ok, null)
.show();
});
}
}).start();
loadConvertedProfile(uri);
return;
}
@@ -411,7 +319,7 @@ public class MainActivity extends AppCompatActivity {
}
try {
loadIniForImport(resolver.openInputStream(uri));
loadIniForImport(getContentResolver().openInputStream(uri));
} catch (FileNotFoundException e) {
new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileImportProfilesFailed)
@@ -419,12 +327,275 @@ public class MainActivity extends AppCompatActivity {
.setPositiveButton(android.R.string.ok, null)
.show();
}
} else if (requestCode == REQUEST_CODE_AI_GENERATOR_TAKE_PHOTO) {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissAIGeneratorMenu());
Bitmap bm = BitmapFactory.decodeFile(aiTempFile.getAbsolutePath());
generateAiModel(bm);
aiTempFile.delete();
aiTempFile = null;
} else if (requestCode == REQUEST_CODE_AI_GENERATOR_CHOOSE_PHOTO) {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissAIGeneratorMenu());
try {
InputStream in = getContentResolver().openInputStream(data.getData());
Bitmap bm = BitmapFactory.decodeStream(in);
generateAiModel(bm);
} catch (Exception e) {
Log.e("ai_generator", "Failed to write to downloads", e);
}
}
}
}
private void loadConvertedProfile(Uri uri) {
String tag = UUID.randomUUID().toString();
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.OrcaConversionPleaseWait).tag(tag));
File f = new File(SliceBeam.getModelCacheDir(), "orca_conv.zip");
IOUtils.IO_POOL.submit(()->{
try {
InputStream in = getContentResolver().openInputStream(uri);
FileOutputStream fos = new FileOutputStream(f);
byte[] buffer = new byte[10240];
int c;
while ((c = in.read(buffer)) != -1) {
fos.write(buffer, 0, c);
}
fos.close();
in.close();
ZipFile zf = new ZipFile(f);
JSONObject bundle = new JSONObject(IOUtils.readString(zf.getInputStream(zf.getEntry("bundle_structure.json"))));
if (!bundle.get("bundle_type").equals("printer config bundle")) {
zf.close();
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileImportProfilesFailed)
.setMessage(R.string.OrcaConversionNotAConfigBundle)
.setPositiveButton(android.R.string.ok, null)
.show());
return;
}
Slic3rConfigWrapper w = new Slic3rConfigWrapper();
if (bundle.has("process_config")) {
JSONArray arr = bundle.getJSONArray("process_config");
List<String> names = new ArrayList<>();
List<String> stripped = new ArrayList<>();
for (int i = 0; i < arr.length(); i++) {
String v = arr.getString(i);
names.add(v);
stripped.add(v.substring(v.indexOf('/') + 1, v.length() - 5));
}
for (String name : names) {
w.printConfigs.add(IOUtils.configJsonToIni(new JSONObject(IOUtils.readString(zf.getInputStream(zf.getEntry(name)))), "process", Slic3rConfigWrapper.PRINT_CONFIG_KEYS, stripped));
}
for (ConfigObject obj : w.printConfigs) {
String inherit = obj.get("inherits");
while (inherit != null) {
ConfigObject _obj = w.findPrint(inherit);
if (_obj == null) throw new IOUtils.MissingProfileException(inherit);
obj.values.remove("inherits");
HashMap<String, String> newMap = new HashMap<>();
newMap.putAll(_obj.values);
newMap.putAll(obj.values);
obj.values = newMap;
inherit = obj.values.get("inherits");
}
}
}
if (bundle.has("filament_config")) {
JSONArray arr = bundle.getJSONArray("filament_config");
List<String> names = new ArrayList<>();
List<String> stripped = new ArrayList<>();
for (int i = 0; i < arr.length(); i++) {
String v = arr.getString(i);
names.add(v);
stripped.add(v.substring(v.indexOf('/') + 1, v.length() - 5));
}
for (String name : names) {
w.filamentConfigs.add(IOUtils.configJsonToIni(new JSONObject(IOUtils.readString(zf.getInputStream(zf.getEntry(name)))), "filament", Slic3rConfigWrapper.FILAMENT_CONFIG_KEYS, stripped));
}
for (ConfigObject obj : w.filamentConfigs) {
String inherit = obj.get("inherits");
while (inherit != null) {
ConfigObject _obj = w.findFilament(inherit);
if (_obj == null) throw new IOUtils.MissingProfileException(inherit);
obj.values.remove("inherits");
HashMap<String, String> newMap = new HashMap<>();
newMap.putAll(_obj.values);
newMap.putAll(obj.values);
obj.values = newMap;
inherit = obj.values.get("inherits");
}
}
}
if (bundle.has("printer_config")) {
JSONArray arr = bundle.getJSONArray("printer_config");
List<String> names = new ArrayList<>();
List<String> stripped = new ArrayList<>();
for (int i = 0; i < arr.length(); i++) {
String v = arr.getString(i);
names.add(v);
stripped.add(v.substring(v.indexOf('/') + 1));
}
for (String name : names) {
w.printerConfigs.add(IOUtils.configJsonToIni(new JSONObject(IOUtils.readString(zf.getInputStream(zf.getEntry(name)))), "machine", Slic3rConfigWrapper.PRINTER_CONFIG_KEYS, stripped));
}
for (ConfigObject obj : w.printerConfigs) {
String inherit = obj.get("inherits");
while (inherit != null) {
ConfigObject _obj = w.findPrinter(inherit);
if (_obj == null) throw new IOUtils.MissingProfileException(inherit);
obj.values.remove("inherits");
HashMap<String, String> newMap = new HashMap<>();
newMap.putAll(_obj.values);
newMap.putAll(obj.values);
obj.values = newMap;
inherit = obj.values.get("inherits");
}
}
}
zf.close();
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
loadIniForImport(new ByteArrayInputStream(w.serialize().getBytes(StandardCharsets.UTF_8)));
} catch (IOUtils.MissingProfileException ep) {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileImportProfilesFailed)
.setMessage(getString(R.string.MenuFileImportProfilesFailedBaseProfileNotFound, ep.profile))
.setPositiveButton(android.R.string.ok, null)
.show());
} catch (Exception e) {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileImportProfilesFailed)
.setMessage(e.toString())
.setPositiveButton(android.R.string.ok, null)
.show());
}
});
}
private void generateAiModel(Bitmap bm) {
IS_GENERATING_AI_MODEL = true;
String uploadTag = UUID.randomUUID().toString();
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuFileAIGeneratorUploading).tag(uploadTag));
IOUtils.IO_POOL.submit(()->{
Bitmap scaled;
if (bm.getWidth() > 1024 || bm.getHeight() > 1024) {
if (bm.getWidth() > bm.getHeight()) {
int w = 1024;
int h = (int) ((float) w * bm.getHeight() / bm.getWidth());
scaled = Bitmap.createScaledBitmap(bm, w, h, true);
} else {
int h = 1024;
int w = (int) ((float) h * bm.getWidth() / bm.getHeight());
scaled = Bitmap.createScaledBitmap(bm, w, h, true);
}
bm.recycle();
} else {
scaled = bm;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
scaled.compress(Bitmap.CompressFormat.PNG, 100, out);
scaled.recycle();
String processTag = UUID.randomUUID().toString();
CloudAPI.INSTANCE.modelsGenerate(Base64.encodeToString(out.toByteArray(), Base64.NO_WRAP), "image/png", new APICallback<InputStream>() {
@Override
public void onResponse(InputStream in) {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(processTag));
String downloadTag = UUID.randomUUID().toString();
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuFileAIGeneratorDownloading).tag(downloadTag));
String fileName = "generated_" + UUID.randomUUID() + ".stl";
File f = new File(SliceBeam.getModelCacheDir(), fileName);
try {
FileOutputStream fos = new FileOutputStream(f);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "application/vnd.ms-pki.stl");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
Uri uri = getContentResolver().insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues);
if (uri != null) {
try {
OutputStream out = getContentResolver().openOutputStream(uri);
byte[] buf = new byte[10240];
int c;
while ((c = in.read(buf)) != -1) {
out.write(buf, 0, c);
fos.write(buf, 0, c);
}
out.close();
} catch (IOException e) {
Log.e("ai_generator", "Failed to write to downloads", e);
}
}
} else {
File downloadsDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
File file = new File(downloadsDirectory, fileName);
try {
FileOutputStream out = new FileOutputStream(file);
byte[] buf = new byte[10240];
int c;
while ((c = in.read(buf)) != -1) {
out.write(buf, 0, c);
fos.write(buf, 0, c);
}
out.close();
} catch (IOException e) {
Log.e("ai_generator", "Failed to write to downloads", e);
}
}
fos.close();
} catch (Exception e) {
Log.e("ai_generator", "Failed to write to downloads", e);
}
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(downloadTag));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileAIGeneratorSavedAs, fileName));
loadFile(f, true);
CloudController.checkGeneratorRemaining();
IS_GENERATING_AI_MODEL = false;
}
@Override
public void onException(Exception e) {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(processTag));
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(MainActivity.this)
.setTitle(R.string.MenuFileAIGeneratorError)
.setMessage(e.toString())
.setPositiveButton(android.R.string.ok, null)
.show());
IS_GENERATING_AI_MODEL = false;
}
});
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(uploadTag));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuFileAIGeneratorProcessing).tag(processTag));
});
}
private void loadIniForImport(InputStream in) {
new Thread(()->{
IOUtils.IO_POOL.submit(()->{
try {
Slic3rConfigWrapper w = new Slic3rConfigWrapper(in);
@@ -450,39 +621,68 @@ public class MainActivity extends AppCompatActivity {
enabledPrinters[i] = true;
}
new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileExportProfilesPrints)
.setMultiChoiceItems(prints, enabledPrints, (dialog, which, isChecked) -> enabledPrints[which] = isChecked)
.setPositiveButton(android.R.string.ok, (d1, w1) -> new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileExportProfilesFilaments)
.setMultiChoiceItems(filaments, enabledFilaments, (dialog, which, isChecked) -> enabledFilaments[which] = isChecked)
.setPositiveButton(android.R.string.ok, (d2, w2) -> new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileExportProfilesPrinters)
.setMultiChoiceItems(printers, enabledPrinters, (dialog, which, isChecked) -> enabledPrinters[which] = isChecked)
.setPositiveButton(android.R.string.ok, (d3, w3) -> {
for (int i = 0; i < enabledPrints.length; i++) {
if (enabledPrints[i]) {
SliceBeam.CONFIG.importPrint(w.printConfigs.get(i));
}
}
for (int i = 0; i < enabledFilaments.length; i++) {
if (enabledFilaments[i]) {
SliceBeam.CONFIG.importFilament(w.filamentConfigs.get(i));
}
}
for (int i = 0; i < enabledPrinters.length; i++) {
if (enabledPrinters[i]) {
SliceBeam.CONFIG.importPrinter(w.printerConfigs.get(i));
}
}
SliceBeam.saveConfig();
})
.setNegativeButton(android.R.string.cancel, null)
.show())
.setNegativeButton(android.R.string.cancel, null)
.show())
.setNegativeButton(android.R.string.cancel, null)
.show();
if (prints.length == 0 && filaments.length == 0 && printers.length == 0) {
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileImportProfilesFailed)
.setMessage(R.string.MenuFileImportProfilesFailedEmpty)
.setPositiveButton(android.R.string.ok, null)
.show());
return;
}
Runnable finish = () -> {
for (int i = 0; i < enabledPrints.length; i++) {
if (enabledPrints[i]) {
SliceBeam.CONFIG.importPrint(w.printConfigs.get(i));
}
}
for (int i = 0; i < enabledFilaments.length; i++) {
if (enabledFilaments[i]) {
SliceBeam.CONFIG.importFilament(w.filamentConfigs.get(i));
}
}
for (int i = 0; i < enabledPrinters.length; i++) {
if (enabledPrinters[i]) {
SliceBeam.CONFIG.importPrinter(w.printerConfigs.get(i));
}
}
SliceBeam.saveConfig();
};
Runnable printersRun = () -> {
if (printers.length == 0) {
finish.run();
return;
}
new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileExportProfilesPrinters)
.setMultiChoiceItems(printers, enabledPrinters, (dialog, which, isChecked) -> enabledPrinters[which] = isChecked)
.setPositiveButton(android.R.string.ok, (d3, w3) -> finish.run())
.setNegativeButton(android.R.string.cancel, null)
.show();
};
Runnable filamentsRun = () -> {
if (filaments.length == 0) {
printersRun.run();
return;
}
new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileExportProfilesFilaments)
.setMultiChoiceItems(filaments, enabledFilaments, (dialog, which, isChecked) -> enabledFilaments[which] = isChecked)
.setPositiveButton(android.R.string.ok, (d2, w2) -> printersRun.run())
.setNegativeButton(android.R.string.cancel, null)
.show();
};
if (prints.length == 0) {
filamentsRun.run();
} else {
new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileExportProfilesPrints)
.setMultiChoiceItems(prints, enabledPrints, (dialog, which, isChecked) -> enabledPrints[which] = isChecked)
.setPositiveButton(android.R.string.ok, (d1, w1) -> filamentsRun.run())
.setNegativeButton(android.R.string.cancel, null)
.show();
}
});
} catch (Exception e) {
Log.e("MainActivity", "Failed to read file", e);
@@ -493,27 +693,68 @@ public class MainActivity extends AppCompatActivity {
.setPositiveButton(android.R.string.ok, null)
.show());
}
}).start();
});
}
private void loadFile(File f, boolean autoorient) {
String tag = UUID.randomUUID().toString();
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuFileOpenFileLoading).tag(tag));
IOUtils.IO_POOL.submit(() -> {
Process.setThreadPriority(-20);
if (delegate.getCurrentFragment() instanceof BedFragment) {
BedFragment fragment = (BedFragment) delegate.getCurrentFragment();
try {
boolean gcode = f.getName().endsWith(".gcode");
if (gcode) {
fragment.loadGCode(f);
} else {
fragment.loadModel(f);
}
fragment.getGlView().queueEvent(new Runnable() {
@Override
public void run() {
Model model = fragment.getGlView().getRenderer().getModel();
if (model == null || fragment.getGlView().getRenderer().getBed() == null) {
fragment.getGlView().queueEvent(this);
return;
}
if (!gcode) {
SliceBeam.EVENT_BUS.fireEvent(new ObjectsListChangedEvent());
}
int i = model.getObjectsCount() - 1;
if (autoorient) {
model.autoOrient(i);
fragment.getGlView().getRenderer().invalidateGlModel(i);
fragment.getGlView().requestRender();
}
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileOpenFileLoaded));
if (model.isBigObject(i)) {
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.WARNING, R.string.MenuFileOpenFileBigObject));
}
}
});
} catch (Slic3rRuntimeError e) {
Log.e("MainActivity", "Failed to load model", e);
f.delete();
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileOpenFileFailed)
.setMessage(e.toString())
.setPositiveButton(android.R.string.ok, null)
.show());
}
}
});
}
private void loadFile(Uri uri) {
if (uri == null) return;
ContentResolver resolver = getContentResolver();
String[] projection = {MediaStore.MediaColumns.DISPLAY_NAME};
Cursor metaCursor = resolver.query(uri, projection, null, null, null);
String fileName = null;
if (metaCursor != null) {
try {
if (metaCursor.moveToFirst()) {
fileName = metaCursor.getString(0);
}
} finally {
metaCursor.close();
}
}
String fileName = IOUtils.getDisplayName(uri);
if (fileName == null) {
new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileOpenFileFailed)
@@ -522,9 +763,26 @@ public class MainActivity extends AppCompatActivity {
.show();
return;
}
if (fileName.endsWith(".orca_printer")) {
loadConvertedProfile(uri);
return;
}
if (fileName.endsWith(".ini")) {
try {
loadIniForImport(resolver.openInputStream(uri));
} catch (FileNotFoundException e) {
new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileImportProfilesFailed)
.setMessage(e.toString())
.setPositiveButton(android.R.string.ok, null)
.show();
}
return;
}
File f = new File(SliceBeam.getModelCacheDir(), fileName);
// TODO: Check if file already exists
new Thread(()->{
IOUtils.IO_POOL.submit(()->{
try {
InputStream in = resolver.openInputStream(uri);
FileOutputStream fos = new FileOutputStream(f);
@@ -534,30 +792,7 @@ public class MainActivity extends AppCompatActivity {
}
fos.close();
in.close();
ViewUtils.postOnMainThread(() -> {
if (delegate.getCurrentFragment() instanceof BedFragment) {
BedFragment fragment = (BedFragment) delegate.getCurrentFragment();
try {
if (f.getName().endsWith(".gcode")) {
fragment.loadGCode(f);
} else {
fragment.loadModel(f);
SliceBeam.EVENT_BUS.fireEvent(new ObjectsListChangedEvent());
}
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileOpenFileLoaded));
} catch (Slic3rRuntimeError e) {
Log.e("MainActivity", "Failed to load model", e);
f.delete();
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileOpenFileFailed)
.setMessage(e.toString())
.setPositiveButton(android.R.string.ok, null)
.show());
}
}
});
loadFile(f, false);
} catch (Exception e) {
Log.e("MainActivity", "Failed to write cache file", e);
@@ -568,7 +803,7 @@ public class MainActivity extends AppCompatActivity {
.setPositiveButton(android.R.string.ok, null)
.show());
}
}).start();
});
}
@Override
@@ -1,21 +1,35 @@
package ru.ytkab0bp.slicebeam;
import static android.opengl.GLES30.*;
import static android.opengl.GLES30.GL_COLOR_BUFFER_BIT;
import static android.opengl.GLES30.GL_DEPTH_BUFFER_BIT;
import static android.opengl.GLES30.GL_DEPTH_TEST;
import static android.opengl.GLES30.glClear;
import static android.opengl.GLES30.glClearColor;
import static android.opengl.GLES30.glDisable;
import static android.opengl.GLES30.glEnable;
import static android.opengl.GLES30.glViewport;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.opengl.GLSurfaceView;
import android.os.Build;
import android.os.Bundle;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.ViewGroup;
@@ -30,12 +44,14 @@ import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.ColorUtils;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.dynamicanimation.animation.FloatValueHolder;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.ViewPager2;
@@ -73,10 +89,17 @@ import javax.microedition.khronos.opengles.GL10;
import cz.msebera.android.httpclient.Header;
import ru.ytkab0bp.eventbus.EventHandler;
import ru.ytkab0bp.slicebeam.cloud.CloudAPI;
import ru.ytkab0bp.slicebeam.cloud.CloudController;
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.components.CloudManageBottomSheet;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.events.BeamServerDataUpdatedEvent;
import ru.ytkab0bp.slicebeam.events.CloudFeaturesUpdatedEvent;
import ru.ytkab0bp.slicebeam.events.CloudLoginStateUpdatedEvent;
import ru.ytkab0bp.slicebeam.events.CloudSyncFinishedEvent;
import ru.ytkab0bp.slicebeam.recycler.BigHeaderItem;
import ru.ytkab0bp.slicebeam.recycler.PreferenceItem;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerAdapter;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import ru.ytkab0bp.slicebeam.recycler.TextHintRecyclerItem;
@@ -94,13 +117,21 @@ import ru.ytkab0bp.slicebeam.view.BeamSwitch;
import ru.ytkab0bp.slicebeam.view.BoostySubsView;
import ru.ytkab0bp.slicebeam.view.FadeRecyclerView;
import ru.ytkab0bp.slicebeam.view.MiniColorView;
import ru.ytkab0bp.slicebeam.view.TextColorImageSpan;
public class SetupActivity extends AppCompatActivity {
public final static String EXTRA_ABOUT = "about";
public final static String EXTRA_BOOSTY_ONLY = "boosty_only";
public final static String EXTRA_CLOUD_PROFILE = "cloud_profile";
public final static String EXTRA_CLOUD_IMPORT_FROM_SETUP = "cloud_import_from_setup";
private final static String TAG = "SetupActivity";
private final static String PRUSA_REPOS_URL = "https://preset-repo-api.prusa3d.com/v1/repos";
private final static List<String> REPOS_URLS = Arrays.asList(
"https://preset-repo-api.prusa3d.com/v1/repos",
"https://raw.githubusercontent.com/utkabobr/SliceBeam/refs/heads/master/.profiledumpsrepo/manifest.json"
);
private final static int REPOS_INDEX = 1;
private final static int PROFILES_INDEX = 2;
private static int BOOSTY_INDEX = 3;
@@ -113,6 +144,7 @@ public class SetupActivity extends AppCompatActivity {
private GLSurfaceView backgroundView;
private GLModel backgroundModel;
private GLShadersManager shadersManager;
private int titleY;
private float backgroundProgress;
@@ -125,6 +157,7 @@ public class SetupActivity extends AppCompatActivity {
private List<ProfilesRepo> repos = new ArrayList<>();
private ReposItem reposItem;
private ProfilesItem profilesItem;
private CloudProfileItem cloudItem;
private boolean isReposLoaded;
private boolean limitRepoFragmentCount = true;
private boolean limitProfileFragmentCount = true;
@@ -133,6 +166,9 @@ public class SetupActivity extends AppCompatActivity {
private Map<ProfilesRepo, List<Slic3rConfigWrapper>> profilesMap = new HashMap<>();
private boolean isProfilesLoaded;
private boolean about;
private boolean boostyOnly;
private boolean cloudProfile;
private boolean cloudImport;
private List<ConfigObject> enabledPrinters = new ArrayList<>();
@@ -149,8 +185,11 @@ public class SetupActivity extends AppCompatActivity {
SliceBeam.EVENT_BUS.registerListener(this);
about = getIntent().getBooleanExtra(EXTRA_ABOUT, false);
boostyOnly = getIntent().getBooleanExtra(EXTRA_BOOSTY_ONLY, false);
cloudProfile = getIntent().getBooleanExtra(EXTRA_CLOUD_PROFILE, false);
cloudImport = getIntent().getBooleanExtra(EXTRA_CLOUD_IMPORT_FROM_SETUP, false);
if (!about) {
if (!about && !boostyOnly && !cloudProfile) {
new BeamAlertDialogBuilder(this)
.setTitle(R.string.IntroEarlyAccess)
.setMessage(R.string.IntroEarlyAccessMessage)
@@ -158,11 +197,15 @@ public class SetupActivity extends AppCompatActivity {
.show();
}
if (boostyOnly || cloudProfile) {
backgroundProgress = 1f;
}
pager = new ViewPager2(this);
adapter = new SimpleRecyclerAdapter() {
@Override
public int getItemCount() {
return about ? 1 : limitRepoFragmentCount ? REPOS_INDEX + 1 : limitProfileFragmentCount ? PROFILES_INDEX + 1 : super.getItemCount();
return about || boostyOnly || cloudProfile ? 1 : limitRepoFragmentCount ? REPOS_INDEX + 1 : limitProfileFragmentCount ? PROFILES_INDEX + 1 : super.getItemCount();
}
};
setItems();
@@ -193,13 +236,15 @@ public class SetupActivity extends AppCompatActivity {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (position == 0) {
if (position == 0 && !boostyOnly && !cloudProfile) {
backgroundProgress = positionOffset;
} else {
backgroundProgress = 1f;
}
if (position == BOOSTY_INDEX) {
if (boostyOnly) {
boostyProgress = 1f;
} else if (position == BOOSTY_INDEX) {
boostyProgress = 1f - positionOffset;
} else if (position == BOOSTY_INDEX - 1) {
boostyProgress = positionOffset;
@@ -329,14 +374,7 @@ public class SetupActivity extends AppCompatActivity {
}
}
title.setTranslationY(ViewUtils.lerp(titleY, (ViewUtils.dp(52) - title.getHeight() * title.getScaleY()) / 2f, backgroundProgress));
float sc = ViewUtils.lerp(1, 22 / 32f, backgroundProgress);
title.setPivotX(title.getWidth() / 2f);
title.setPivotY(0);
title.setScaleX(sc);
title.setScaleY(sc);
int color = ColorUtils.blendARGB(ThemesRepo.getColor(R.attr.textColorOnAccent), ThemesRepo.getColor(android.R.attr.colorAccent), backgroundProgress - boostyProgress);
title.setTextColor(color);
invalidateTitleY();
backgroundView.requestRender();
}
});
@@ -349,7 +387,7 @@ public class SetupActivity extends AppCompatActivity {
super.onSizeChanged(w, h, oldw, oldh);
titleY = h / 4;
title.setTranslationY(ViewUtils.lerp(titleY, title.getPaddingTop(), backgroundProgress));
invalidateTitleY();
}
};
fl.setClipChildren(false);
@@ -360,20 +398,23 @@ public class SetupActivity extends AppCompatActivity {
super.surfaceDestroyed(holder);
backgroundModel.release();
backgroundModel = null;
GLShadersManager.clearShaders();
shadersManager.clearShaders();
shadersManager = null;
}
};
backgroundView.setEGLContextClientVersion(3);
backgroundView.setRenderer(new GLSurfaceView.Renderer() {
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
backgroundModel = new GLModel();
backgroundModel.initBackgroundTriangles();
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
glViewport(0, 0, width, height);
if (backgroundModel == null) {
backgroundModel = new GLModel();
backgroundModel.initBackgroundTriangles();
shadersManager = new GLShadersManager();
}
}
private float time;
@@ -389,7 +430,7 @@ public class SetupActivity extends AppCompatActivity {
if (backgroundModel != null) {
glDisable(GL_DEPTH_TEST);
GLShaderProgram shader = GLShadersManager.get(GLShadersManager.SHADER_BEAM_INTRO);
GLShaderProgram shader = shadersManager.get(GLShadersManager.SHADER_BEAM_INTRO);
shader.startUsing();
int topColor = ThemesRepo.getColor(android.R.attr.colorAccent);
int bottomColor = ThemesRepo.getColor(android.R.attr.windowBackground);
@@ -397,10 +438,13 @@ public class SetupActivity extends AppCompatActivity {
topColor = ColorUtils.blendARGB(bottomColor, ThemesRepo.getColor(R.attr.boostyColorTop), boostyProgress);
bottomColor = ColorUtils.blendARGB(bottomColor, ThemesRepo.getColor(R.attr.boostyColorBottom), boostyProgress);
}
if (cloudProfile) {
bottomColor = ColorUtils.blendARGB(bottomColor, topColor, 0.5f);
}
shader.setUniformColor("top_color", topColor);
shader.setUniformColor("bottom_color", bottomColor);
shader.setUniform("progress", backgroundProgress - (boostyProgress != 0 ? 1.2f : 0));
shader.setUniform("progress", backgroundProgress - (cloudProfile ? 1.4f : 0) - (boostyProgress != 0 ? 1.2f : 0));
shader.setUniform("time", time);
backgroundModel.render();
shader.stopUsing();
@@ -414,7 +458,7 @@ public class SetupActivity extends AppCompatActivity {
title = new TextView(this);
title.setGravity(Gravity.CENTER);
title.setTypeface(Typeface.DEFAULT_BOLD);
title.setText(R.string.AppName);
title.setText(cloudProfile ? R.string.SettingsCloudManageTitle : R.string.AppName);
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 32);
title.setTextColor(ThemesRepo.getColor(R.attr.textColorOnAccent));
title.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL));
@@ -439,6 +483,17 @@ public class SetupActivity extends AppCompatActivity {
}
}
private void invalidateTitleY() {
float sc = ViewUtils.lerp(1, 22 / 32f, backgroundProgress);
title.setPivotX(title.getWidth() / 2f);
title.setPivotY(0);
title.setScaleX(sc);
title.setScaleY(sc);
int color = ColorUtils.blendARGB(ThemesRepo.getColor(R.attr.textColorOnAccent), ThemesRepo.getColor(android.R.attr.colorAccent), cloudProfile ? 0f : backgroundProgress - boostyProgress);
title.setTextColor(color);
title.setTranslationY(ViewUtils.lerp(titleY, (ViewUtils.dp(52) - title.getHeight() * title.getScaleY()) / 2f, backgroundProgress));
}
@Override
protected void onDestroy() {
super.onDestroy();
@@ -448,7 +503,7 @@ public class SetupActivity extends AppCompatActivity {
@EventHandler(runOnMainThread = true)
public void onDataUpdated(BeamServerDataUpdatedEvent e) {
if (!about) {
if (!about && !boostyOnly && !cloudProfile) {
boolean wasBoosty = BOOSTY_INDEX != -1;
if (wasBoosty != BeamServerData.isBoostyAvailable()) {
setItems();
@@ -456,8 +511,43 @@ public class SetupActivity extends AppCompatActivity {
}
}
@SuppressLint("NotifyDataSetChanged")
@EventHandler(runOnMainThread = true)
public void onCloudSyncFinished(CloudSyncFinishedEvent e) {
if (cloudProfile && Prefs.getCloudAPIToken() != null && cloudImport) {
finish();
}
if (!about && !boostyOnly && !cloudProfile) {
if (Prefs.getCloudAPIToken() != null) {
limitRepoFragmentCount = false;
limitProfileFragmentCount = false;
pager.getAdapter().notifyDataSetChanged();
pager.setCurrentItem(pager.getAdapter().getItemCount() - 1);
}
}
}
@EventHandler(runOnMainThread = true)
public void onCloudAuthStateUpdated(CloudLoginStateUpdatedEvent e) {
if (cloudProfile) {
cloudItem.bindLoginButton(true);
cloudItem.bindFeatures();
}
}
@EventHandler(runOnMainThread = true)
public void onCloudFeaturesUpdated(CloudFeaturesUpdatedEvent e) {
if (!about && !boostyOnly && !cloudProfile) {
reposItem.onCloudInfoUpdated();
}
}
private void setItems() {
if (about) {
if (cloudProfile){
adapter.setItems(Collections.singletonList(cloudItem = new CloudProfileItem()));
} else if (boostyOnly) {
adapter.setItems(Collections.singletonList(new BoostyItem()));
} else if (about) {
adapter.setItems(Collections.singletonList(new AboutItem()));
} else {
List<SimpleRecyclerItem> items = new ArrayList<>(Arrays.asList(
@@ -488,49 +578,70 @@ public class SetupActivity extends AppCompatActivity {
private void loadRepos(boolean fromPage) {
isLoading = true;
client.get(PRUSA_REPOS_URL, new AsyncHttpResponseHandler() {
repos.clear();
List<String> finishedIndexes = new ArrayList<>();
Map<String, List<ProfilesRepo>> reposMap = new HashMap<String, List<ProfilesRepo>>() {
@Nullable
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
isLoading = false;
try {
JSONArray arr = new JSONArray(new String(responseBody));
for (int i = 0; i < arr.length(); i++) {
JSONObject obj = arr.getJSONObject(i);
if (obj.getString("id").endsWith("-fff")) {
ProfilesRepo r = new ProfilesRepo();
r.url = obj.getString("url");
r.name = obj.getString("name");
r.description = obj.getString("description");
r.indexUrl = obj.getString("index_url");
repos.add(r);
public List<ProfilesRepo> get(@Nullable Object key) {
List<ProfilesRepo> list = super.get(key);
if (list == null) put((String) key, list = new ArrayList<>());
return list;
}
};
for (String repo : REPOS_URLS) {
client.get(repo, new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
finishedIndexes.add(repo);
try {
JSONArray arr = new JSONArray(new String(responseBody));
for (int i = 0; i < arr.length(); i++) {
JSONObject obj = arr.getJSONObject(i);
if (obj.getString("id").endsWith("-fff")) {
ProfilesRepo r = new ProfilesRepo();
r.url = obj.getString("url");
r.name = obj.getString("name");
r.description = obj.getString("description");
r.indexUrl = obj.getString("index_url");
reposMap.get(repo).add(r);
}
}
if (finishedIndexes.size() == REPOS_URLS.size()) {
// Filter in the right way
for (String repo : REPOS_URLS) {
repos.addAll(reposMap.get(repo));
}
ViewUtils.postOnMainThread(() -> {
isLoading = false;
if (fromPage) {
reposItem.onReposLoaded();
}
pager.setUserInputEnabled(true);
isReposLoaded = true;
});
}
} catch (JSONException e) {
throw new RuntimeException(e);
}
ViewUtils.postOnMainThread(() -> {
if (fromPage) {
reposItem.onReposLoaded();
}
pager.setUserInputEnabled(true);
isReposLoaded = true;
});
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
isLoading = false;
Log.e(TAG, "Failed to load repos", error);
if (fromPage) {
ViewUtils.postOnMainThread(() -> {
Toast.makeText(SliceBeam.INSTANCE, R.string.IntroFailedToLoadRepos, Toast.LENGTH_SHORT).show();
fakeScroll(-1);
pager.setUserInputEnabled(true);
});
@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
isLoading = false;
Log.e(TAG, "Failed to load repos", error);
if (fromPage) {
ViewUtils.postOnMainThread(() -> {
Toast.makeText(SliceBeam.INSTANCE, R.string.IntroFailedToLoadRepos, Toast.LENGTH_SHORT).show();
fakeScroll(-1);
pager.setUserInputEnabled(true);
});
}
}
}
});
});
}
}
@Override
@@ -570,6 +681,331 @@ public class SetupActivity extends AppCompatActivity {
fakeScroller.start();
}
private final class CloudProfileItem extends SimpleRecyclerItem<View> {
private FrameLayout buttonView;
private TextView buttonText;
private ProgressBar buttonProgress;
private FadeRecyclerView recyclerView;
@Override
public View onCreateView(Context ctx) {
LinearLayout ll = new LinearLayout(ctx);
ll.setOrientation(LinearLayout.VERTICAL);
ll.setPadding(0, ViewUtils.dp(42), 0, 0);
TextView title = new TextView(ctx);
title.setTextColor(ThemesRepo.getColor(R.attr.textColorOnAccent));
title.setText(R.string.SettingsCloudManageDescription);
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
title.setGravity(Gravity.CENTER);
title.setPadding(ViewUtils.dp(12), 0, ViewUtils.dp(12), 0);
ll.addView(title);
FrameLayout fl = new FrameLayout(ctx);
recyclerView = new FadeRecyclerView(ctx);
recyclerView.setBitmapMode();
recyclerView.setAdapter(adapter = new SimpleRecyclerAdapter());
recyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER);
fl.addView(recyclerView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
bindFeatures();
ll.addView(fl, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
TextView tosButton = new TextView(ctx);
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(ctx.getString(R.string.SettingsCloudManageTermsOfService)).append(" ");
Drawable dr = ContextCompat.getDrawable(ctx, R.drawable.external_link_outline_24);
int size = ViewUtils.dp(16);
dr.setBounds(0, 0, size, size);
sb.append("d", new TextColorImageSpan(dr, ViewUtils.dp(2f)), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
tosButton.setText(sb);
tosButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
tosButton.setTextColor(Color.WHITE);
tosButton.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
tosButton.setGravity(Gravity.CENTER);
tosButton.setPadding(ViewUtils.dp(12), ViewUtils.dp(8), ViewUtils.dp(12), ViewUtils.dp(8));
tosButton.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
tosButton.setOnClickListener(v -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://beam3d.ru/slicebeam_cloud_tos.html"))));
ll.addView(tosButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)) {{
leftMargin = rightMargin = ViewUtils.dp(16);
bottomMargin = ViewUtils.dp(8);
}});
buttonView = new FrameLayout(ctx);
buttonView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), ThemesRepo.getColor(android.R.attr.colorAccent), 16));
buttonText = new TextView(ctx);
buttonText.setTextColor(ThemesRepo.getColor(R.attr.textColorOnAccent));
buttonText.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
buttonText.setGravity(Gravity.CENTER);
buttonText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
buttonView.addView(buttonText, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
buttonProgress = new ProgressBar(ctx);
buttonProgress.setIndeterminateTintList(ColorStateList.valueOf(ThemesRepo.getColor(R.attr.textColorOnAccent)));
buttonView.addView(buttonProgress, new FrameLayout.LayoutParams(ViewUtils.dp(28), ViewUtils.dp(28), Gravity.CENTER));
bindLoginButton(false);
ll.addView(buttonView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)) {{
leftMargin = rightMargin = ViewUtils.dp(16);
bottomMargin = ViewUtils.dp(16);
}});
ll.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return ll;
}
private void bindFeatures() {
List<SimpleRecyclerItem> items = new ArrayList<>();
if (CloudController.getUserFeatures() != null) {
for (CloudAPI.SubscriptionLevel lvl : CloudController.getUserFeatures().levels) {
items.add(new CloudSubscriptionLevel(lvl));
}
}
adapter.setItems(items);
}
private void bindLoginButton(boolean animate) {
boolean loggedIn = Prefs.getCloudAPIToken() != null;
boolean loading = !loggedIn && CloudController.isLoggingIn();
boolean wasLoading = buttonProgress.getTag() != null;
if (animate) {
if (wasLoading != loading) {
buttonProgress.setTag(loading ? 1 : null);
buttonProgress.animate().cancel();
buttonProgress.animate().scaleX(loading ? 1f : 0.4f).scaleY(loading ? 1f : 0.4f).alpha(loading ? 1f : 0f).setDuration(150).setInterpolator(ViewUtils.CUBIC_INTERPOLATOR).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
if (loading) {
buttonProgress.setVisibility(View.VISIBLE);
buttonProgress.setAlpha(0f);
buttonProgress.setScaleX(0.4f);
buttonProgress.setScaleY(0.4f);
}
}
@Override
public void onAnimationEnd(Animator animation) {
if (!loading) {
buttonProgress.setVisibility(View.GONE);
}
}
}).start();
buttonText.animate().cancel();
buttonText.animate().scaleX(!loading ? 1f : 0.4f).scaleY(!loading ? 1f : 0.4f).alpha(!loading ? 1f : 0f).setDuration(150).setInterpolator(ViewUtils.CUBIC_INTERPOLATOR).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
if (!loading) {
buttonText.setVisibility(View.VISIBLE);
buttonText.setAlpha(0f);
buttonText.setScaleX(0.4f);
buttonText.setScaleY(0.4f);
}
}
@Override
public void onAnimationEnd(Animator animation) {
if (loading) {
buttonText.setVisibility(View.GONE);
}
}
}).start();
}
} else {
buttonProgress.setTag(loading ? 1 : null);
buttonProgress.setVisibility(loading ? View.VISIBLE : View.GONE);
buttonText.setVisibility(loading ? View.GONE : View.VISIBLE);
}
buttonText.setText(loggedIn ? R.string.SettingsCloudManageButtonManage : R.string.SettingsCloudManageButtonLogIn);
buttonView.setOnClickListener(v-> {
if (loading) {
new BeamAlertDialogBuilder(v.getContext())
.setTitle(R.string.SettingsCloudManageButtonLogInCancelTitle)
.setMessage(R.string.SettingsCloudManageButtonLogInCancel)
.setNegativeButton(R.string.No, null)
.setPositiveButton(R.string.Yes, (dialog, which) -> CloudController.cancelLogin())
.show();
} else if (Prefs.getCloudAPIToken() != null) {
new CloudManageBottomSheet(v.getContext()).show();
} else {
CloudController.beginLogin();
}
});
}
}
private final static class CloudSubscriptionLevel extends SimpleRecyclerItem<CloudSubscriptionLevel.LevelHolderView> {
private CloudAPI.SubscriptionLevel level;
private CloudSubscriptionLevel(CloudAPI.SubscriptionLevel level) {
this.level = level;
}
@Override
public LevelHolderView onCreateView(Context ctx) {
return new LevelHolderView(ctx);
}
@Override
public void onBindView(LevelHolderView view) {
view.bind(this);
}
public final static class LevelHolderView extends LinearLayout implements IThemeView {
private ImageView icon;
private TextView title;
private TextView price;
private RecyclerView featuresLayout;
private SimpleRecyclerAdapter featuresAdapter;
public LevelHolderView(@NonNull Context context) {
super(context);
setOrientation(VERTICAL);
setPadding(0, ViewUtils.dp(16), 0, ViewUtils.dp(8));
LinearLayout inner = new LinearLayout(context);
inner.setOrientation(HORIZONTAL);
inner.setGravity(Gravity.CENTER_VERTICAL);
inner.setPadding(ViewUtils.dp(28), 0, ViewUtils.dp(28), 0);
addView(inner, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
bottomMargin = ViewUtils.dp(8);
}});
icon = new ImageView(context);
inner.addView(icon, new LayoutParams(ViewUtils.dp(26), ViewUtils.dp(26)));
title = new TextView(context);
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
title.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
inner.addView(title, new LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f) {{
leftMargin = ViewUtils.dp(12);
}});
price = new TextView(context);
price.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
price.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
inner.addView(price);
featuresLayout = new RecyclerView(context) {
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return false;
}
@Override
protected boolean dispatchHoverEvent(MotionEvent event) {
return false;
}
};
featuresLayout.setLayoutManager(new LinearLayoutManager(context));
featuresLayout.setAdapter(featuresAdapter = new SimpleRecyclerAdapter());
addView(featuresLayout, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
topMargin = ViewUtils.dp(3);
leftMargin = rightMargin = ViewUtils.dp(16);
bottomMargin = ViewUtils.dp(8);
}});
setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
leftMargin = rightMargin = ViewUtils.dp(12);
topMargin = ViewUtils.dp(12);
}});
onApplyTheme();
}
public void bind(CloudSubscriptionLevel item) {
CloudAPI.SubscriptionLevel lvl = item.level;
title.setText(lvl.title);
price.setText(lvl.price);
if (lvl.level <= 0) {
icon.setImageResource(R.drawable.zero_ruble_outline_28);
price.setText(R.string.SettingsCloudManageFree);
} else if (lvl.level == 1) {
icon.setImageResource(R.drawable.stars_outline_28);
} else {
icon.setImageResource(R.drawable.cloud_plus_outline_28);
}
List<SimpleRecyclerItem> items = new ArrayList<>();
CloudAPI.UserFeatures features = CloudController.getUserFeatures();
CloudAPI.UserInfo info = CloudController.getUserInfo();
Context ctx = getContext();
if (!BuildConfig.IS_GOOGLE_PLAY && features.earlyAccessLevel != -1 && lvl.level >= features.earlyAccessLevel) {
items.add(new PreferenceItem()
.setForceDark(true)
.setPaddings(ViewUtils.dp(8))
.setIcon(R.drawable.clock_circle_dashed_outline_24)
.setTitle(ctx.getString(R.string.SettingsCloudManageFeatureEarlyAccess))
.setSubtitle(ctx.getString(R.string.SettingsCloudManageFeatureEarlyAccessDescription)));
}
if (features.syncRequiredLevel != -1 && lvl.level >= features.syncRequiredLevel) {
items.add(new PreferenceItem()
.setForceDark(true)
.setPaddings(ViewUtils.dp(8))
.setIcon(R.drawable.sync_outline_28)
.setTitle(ctx.getString(R.string.SettingsCloudManageFeatureCloudSync))
.setSubtitle(ctx.getString(R.string.SettingsCloudManageFeatureCloudSyncDescription)));
}
if (features.aiGeneratorRequiredLevel != -1 && lvl.level >= features.aiGeneratorRequiredLevel) {
items.add(new PreferenceItem()
.setForceDark(true)
.setPaddings(ViewUtils.dp(8))
.setIcon(R.drawable.brain_outline_28)
.setTitle(ctx.getString(R.string.SettingsCloudManageFeatureAIGenerator))
.setSubtitle(ctx.getString(R.string.SettingsCloudManageFeatureAIGeneratorDescription, features.aiGeneratorModelsPerMonth)));
}
if (lvl.level > 0) {
items.add(new PreferenceItem()
.setForceDark(true)
.setPaddings(ViewUtils.dp(8))
.setIcon(R.drawable.box_heart_outline_28)
.setTitle(ctx.getString(R.string.SettingsCloudManageFeatureFreeForAll))
.setSubtitle(ctx.getString(R.string.SettingsCloudManageFeatureFreeForAllDescription)));
}
featuresAdapter.setItems(items);
featuresLayout.setVisibility(items.isEmpty() ? View.GONE : View.VISIBLE);
boolean subscribed = lvl.level > 0 && info != null && lvl.level == info.currentLevel;
boolean allowSubscribe = lvl.level > 0 && (info == null || lvl.level > info.currentLevel);
if (subscribed) {
price.setText(R.string.SettingsCloudManageSubscribed);
}
price.setVisibility(allowSubscribe || subscribed ? View.VISIBLE : View.GONE);
setOnClickListener(v -> {
if (subscribed) {
v.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(lvl.manageUrl)));
} else {
new BeamAlertDialogBuilder(getContext())
.setTitle(lvl.title)
.setMessage(R.string.SettingsCloudManageLevelRedirectMessage)
.setPositiveButton(android.R.string.ok, (dialog, which) -> v.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(lvl.subscribeOrUpgradeUrl))))
.setNegativeButton(R.string.SettingsCloudManageLevelRedirectAlreadySubscribed, (dialog, which) -> v.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(features.alreadySubscribedInfoUrl))))
.show();
}
});
setClickable(allowSubscribe || subscribed);
onApplyTheme();
}
@Override
public void onApplyTheme() {
int accent = ThemesRepo.getColor(android.R.attr.colorAccent);
if (ColorUtils.calculateLuminance(accent) >= 0.6f) {
accent = ColorUtils.blendARGB(accent, Color.BLACK, 0.075f);
}
boolean tooLight = ColorUtils.calculateLuminance(accent) >= 0.6f;
title.setTextColor(0xffffffff);
price.setTextColor(0xffffffff);
icon.setImageTintList(ColorStateList.valueOf(0xffffffff));
featuresLayout.setBackground(ViewUtils.createRipple(0, tooLight ? 0x33ffffff : 0x21ffffff, 24));
setBackground(ViewUtils.createRipple(0x21000000, ColorUtils.blendARGB(0xffffffff, accent, tooLight ? 0.9f : 0.75f), 32));
}
}
}
private final class AboutItem extends SimpleRecyclerItem<View> {
@Override
@@ -699,6 +1135,8 @@ public class SetupActivity extends AppCompatActivity {
private ProgressBar progressBar;
private FrameLayout loadedLayout;
private SimpleRecyclerAdapter adapter;
private TextView cloudImportView;
private TextView cloudOrView;
private TextView customProfileView;
private TextView buttonView;
@@ -715,10 +1153,29 @@ public class SetupActivity extends AppCompatActivity {
LinearLayout ll = new LinearLayout(ctx);
ll.setOrientation(LinearLayout.VERTICAL);
cloudImportView = new TextView(ctx);
cloudImportView.setVisibility(View.GONE);
cloudImportView.setText(R.string.IntroImportFromCloud);
cloudImportView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
cloudImportView.setGravity(Gravity.CENTER);
cloudImportView.setPadding(ViewUtils.dp(12), ViewUtils.dp(8), ViewUtils.dp(12), ViewUtils.dp(8));
cloudImportView.setOnClickListener(v -> startActivity(new Intent(v.getContext(), SetupActivity.class).putExtra(SetupActivity.EXTRA_CLOUD_PROFILE, true).putExtra(SetupActivity.EXTRA_CLOUD_IMPORT_FROM_SETUP, true)));
ll.addView(cloudImportView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(48)) {{
leftMargin = rightMargin = ViewUtils.dp(16);
}});
cloudOrView = new TextView(ctx);
cloudOrView.setVisibility(View.GONE);
cloudOrView.setText(R.string.IntroImportOr);
cloudOrView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
cloudOrView.setGravity(Gravity.CENTER);
ll.addView(cloudOrView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
leftMargin = rightMargin = ViewUtils.dp(16);
}});
customProfileView = new TextView(ctx);
customProfileView.setText(R.string.IntroCustomProfile);
customProfileView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
customProfileView.setTextColor(ThemesRepo.getColor(android.R.attr.colorAccent));
customProfileView.setGravity(Gravity.CENTER);
customProfileView.setPadding(ViewUtils.dp(12), ViewUtils.dp(8), ViewUtils.dp(12), ViewUtils.dp(8));
customProfileView.setOnClickListener(v -> {
@@ -727,8 +1184,9 @@ public class SetupActivity extends AppCompatActivity {
SetupActivity.this.adapter.notifyItemRangeInserted(REPOS_INDEX + 1, SetupActivity.this.adapter.getItemCount() - REPOS_INDEX - 1);
scrollToNext();
});
ll.addView(customProfileView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
bottomMargin = ViewUtils.dp(12);
ll.addView(customProfileView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(48)) {{
leftMargin = rightMargin = ViewUtils.dp(16);
bottomMargin = ViewUtils.dp(6);
}});
buttonView = new TextView(ctx);
@@ -775,7 +1233,13 @@ public class SetupActivity extends AppCompatActivity {
@Override
public void onBindView(View view) {
progressBar.setIndeterminateTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.colorAccent)));
cloudImportView.setTextColor(ThemesRepo.getColor(android.R.attr.colorAccent));
cloudImportView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
cloudImportView.setVisibility(BeamServerData.isCloudAvailable() ? View.VISIBLE : View.GONE);
cloudOrView.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
cloudOrView.setVisibility(BeamServerData.isCloudAvailable() ? View.VISIBLE : View.GONE);
customProfileView.setTextColor(ThemesRepo.getColor(android.R.attr.colorAccent));
customProfileView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
buttonView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), ThemesRepo.getColor(android.R.attr.colorAccent), 16));
if (adapter.getItemCount() == 0 && isReposLoaded) {
@@ -798,6 +1262,13 @@ public class SetupActivity extends AppCompatActivity {
}
}
public void onCloudInfoUpdated() {
if (cloudImportView != null) {
cloudImportView.setVisibility(BeamServerData.isCloudAvailable() ? View.VISIBLE : View.GONE);
cloudOrView.setVisibility(BeamServerData.isCloudAvailable() ? View.VISIBLE : View.GONE);
}
}
public void onReposLoaded() {
List<SimpleRecyclerItem> items = new ArrayList<>(repos);
items.add(new TextHintRecyclerItem(SliceBeam.INSTANCE.getString(R.string.IntroSelectRepos)));
@@ -982,25 +1453,42 @@ public class SetupActivity extends AppCompatActivity {
}});
TextView subscribeButton = new TextView(ctx);
subscribeButton.setText(R.string.IntroBoostySupport);
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(ctx.getString(R.string.IntroBoostySupport)).append(" ");
Drawable dr = ContextCompat.getDrawable(ctx, R.drawable.external_link_outline_24);
int size = ViewUtils.dp(16);
dr.setBounds(0, 0, size, size);
sb.append("d", new TextColorImageSpan(dr, ViewUtils.dp(2f)), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
subscribeButton.setText(sb);
subscribeButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
subscribeButton.setTextColor(ThemesRepo.getColor(R.attr.boostyColorTop));
subscribeButton.setTextColor(Color.WHITE);
subscribeButton.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
subscribeButton.setGravity(Gravity.CENTER);
subscribeButton.setPadding(ViewUtils.dp(12), ViewUtils.dp(8), ViewUtils.dp(12), ViewUtils.dp(8));
subscribeButton.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
subscribeButton.setOnClickListener(v -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://boosty.to/ytkab0bp"))));
ll.addView(subscribeButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
bottomMargin = ViewUtils.dp(12);
ll.addView(subscribeButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)) {{
leftMargin = rightMargin = ViewUtils.dp(16);
bottomMargin = ViewUtils.dp(8);
}});
TextView buttonView = new TextView(ctx);
buttonView.setText(R.string.IntroNext);
if (boostyOnly) {
buttonView.setText(android.R.string.ok);
} else {
buttonView.setText(R.string.IntroNext);
}
buttonView.setTextColor(ThemesRepo.getColor(R.attr.textColorOnAccent));
buttonView.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
buttonView.setGravity(Gravity.CENTER);
buttonView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
buttonView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), ThemesRepo.getColor(R.attr.boostyColorTop), 16));
buttonView.setOnClickListener(v-> scrollToNext());
buttonView.setOnClickListener(v-> {
if (boostyOnly) {
finish();
return;
}
scrollToNext();
});
ll.addView(buttonView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)) {{
leftMargin = rightMargin = ViewUtils.dp(16);
bottomMargin = ViewUtils.dp(16);
@@ -1141,11 +1629,13 @@ public class SetupActivity extends AppCompatActivity {
}
}
try {
SliceBeam.getCurrentConfigFile().delete();
SliceBeam.CONFIG = cfg;
FileOutputStream fos = new FileOutputStream(SliceBeam.getConfigFile());
fos.write(cfg.serialize().getBytes(StandardCharsets.UTF_8));
fos.close();
if (Prefs.getCloudAPIToken() == null || SliceBeam.CONFIG == null) {
SliceBeam.getCurrentConfigFile().delete();
SliceBeam.CONFIG = cfg;
FileOutputStream fos = new FileOutputStream(SliceBeam.getConfigFile());
fos.write(cfg.serialize().getBytes(StandardCharsets.UTF_8));
fos.close();
}
startActivity(new Intent(SetupActivity.this, MainActivity.class));
finish();
@@ -4,10 +4,8 @@ import android.annotation.SuppressLint;
import android.app.Application;
import android.content.Intent;
import android.util.Log;
import android.webkit.WebView;
import org.json.JSONException;
import org.json.JSONObject;
import com.instacart.truetime.time.TrueTimeImpl;
import java.io.File;
import java.io.FileOutputStream;
@@ -15,19 +13,32 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
import ru.ytkab0bp.eventbus.EventBus;
import ru.ytkab0bp.slicebeam.boot.AppBoot;
import ru.ytkab0bp.slicebeam.boot.BeamServerDataTask;
import ru.ytkab0bp.slicebeam.boot.CheckUpdateJsonTask;
import ru.ytkab0bp.slicebeam.boot.ClearModelCacheTask;
import ru.ytkab0bp.slicebeam.boot.CloudInitTask;
import ru.ytkab0bp.slicebeam.boot.EventBusTask;
import ru.ytkab0bp.slicebeam.boot.LoadSlic3rConfigTask;
import ru.ytkab0bp.slicebeam.boot.PrefsTask;
import ru.ytkab0bp.slicebeam.boot.PrintConfigWarmupTask;
import ru.ytkab0bp.slicebeam.boot.TrueTimeTask;
import ru.ytkab0bp.slicebeam.boot.VibrationUtilsTask;
import ru.ytkab0bp.slicebeam.cloud.CloudController;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.slic3r.ConfigOptionDef;
import ru.ytkab0bp.slicebeam.slic3r.PrintConfigDef;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.VibrationUtils;
public class SliceBeam extends Application {
public static SliceBeam INSTANCE;
public static EventBus EVENT_BUS = EventBus.newBus("main");
public static TrueTimeImpl TRUE_TIME;
public static Slic3rConfigWrapper CONFIG;
public static int CONFIG_UID = 0;
public static BeamServerData SERVER_DATA;
@@ -38,36 +49,18 @@ public class SliceBeam extends Application {
public void onCreate() {
super.onCreate();
INSTANCE = this;
EventBus.registerImpl(this);
Prefs.init(this);
VibrationUtils.init(this);
tryCheckInfo();
PrintConfigDef.getInstance();
try {
getAssets().open("update.json").close();
hasUpdateInfo = true;
} catch (IOException e) {
hasUpdateInfo = false;
}
File cache = SliceBeam.getModelCacheDir();
if (cache.exists()) {
for (File f : cache.listFiles()) {
f.delete();
}
}
File cfgFile = getConfigFile();
getCurrentConfigFile().delete();
if (cfgFile.exists()) {
try {
CONFIG = new Slic3rConfigWrapper(cfgFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG);
AppBoot.run(Arrays.asList(
new EventBusTask(),
new PrefsTask(),
new VibrationUtilsTask(),
new TrueTimeTask(),
new BeamServerDataTask(),
new PrintConfigWarmupTask(),
new CheckUpdateJsonTask(),
new ClearModelCacheTask(),
new LoadSlic3rConfigTask(),
new CloudInitTask()
));
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
@@ -81,17 +74,6 @@ public class SliceBeam extends Application {
});
}
private static void tryCheckInfo() {
try {
SERVER_DATA = new BeamServerData(new JSONObject(Prefs.getBeamServerData()));
} catch (JSONException e) {
throw new RuntimeException(e);
}
if (System.currentTimeMillis() - Prefs.getLastCheckedInfo() >= 86400000L) {
BeamServerData.load();
}
}
public static void saveConfig() {
SliceBeam.CONFIG_UID++;
File f = getConfigFile();
@@ -104,6 +86,7 @@ public class SliceBeam extends Application {
} catch (Exception e) {
Log.e("Config", "Failed to save config", e);
}
CloudController.notifyDataChanged();
}
public static File getModelCacheDir() {
@@ -122,6 +105,7 @@ public class SliceBeam extends Application {
if (SliceBeam.CONFIG.findPrint(SliceBeam.CONFIG.presets.get("print")) != null) {
singleObject.values.putAll(SliceBeam.CONFIG.findPrint(SliceBeam.CONFIG.presets.get("print")).values);
}
// TODO: MMU. Detect by printerConfig#getExtruderCount()
if (SliceBeam.CONFIG.findFilament(SliceBeam.CONFIG.presets.get("filament")) != null) {
singleObject.values.putAll(SliceBeam.CONFIG.findFilament(SliceBeam.CONFIG.presets.get("filament")).values);
}
@@ -0,0 +1,137 @@
package ru.ytkab0bp.slicebeam.boot;
import android.util.Log;
import android.util.SparseBooleanArray;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import ru.ytkab0bp.slicebeam.BuildConfig;
public class AppBoot {
private final static String TAG = "boot";
static ExecutorService executor = Executors.newCachedThreadPool();
static List<BootTask> tasks;
static List<Runnable> pendingMain = new ArrayList<>();
static List<BootTask> pendingTasks = new ArrayList<>();
static SparseBooleanArray completed = new SparseBooleanArray();
static CountDownLatch latch;
public static void run(List<BootTask> tasks) {
long start = System.currentTimeMillis();
AppBoot.tasks = tasks;
int size = tasks.size();
for (int i = 0, s = tasks.size(); i < s; i++) {
BootTask task = tasks.get(i);
if (task.nonCritical) {
if (!task.workerThread) {
throw new IllegalArgumentException("Can't schedule non-critical task on main thread");
}
size--;
}
}
AppBoot.latch = new CountDownLatch(size);
for (int i = 0, s = tasks.size(); i < s; i++) {
BootTask task = tasks.get(i);
task.index = i;
tryRunTask(task, true, false);
}
try {
while (!latch.await(10, TimeUnit.MILLISECONDS)) {
if (!pendingMain.isEmpty()) {
List<Runnable> clone = new ArrayList<>(pendingMain);
for (Runnable r : clone) {
r.run();
}
pendingMain.removeAll(clone);
}
}
if (BuildConfig.DEBUG) {
Log.d(TAG, "Boot in " + (System.currentTimeMillis() - start) + "ms");
}
tryShutdown();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
private static void tryShutdown() {
if (completed.size() == tasks.size()) {
executor.shutdown();
executor = null;
tasks = null;
pendingMain = null;
pendingTasks = null;
completed = null;
latch = null;
}
}
private static void tryRunTask(BootTask task, boolean fromMain, boolean isContinue) {
if (checkDependencies(task.dependencies)) {
Runnable r = () -> {
try {
task.run.run();
} catch (Exception e) {
Log.e(TAG, "Error while executing boot task", e);
}
completed.put(task.index, true);
if (BuildConfig.DEBUG) {
Log.d(TAG, "Finish " + task);
}
if (!task.nonCritical) {
latch.countDown();
} else {
tryShutdown();
}
if (!isContinue) {
continueTasks(fromMain);
}
};
if (task.workerThread) {
executor.submit(r);
} else {
if (fromMain) {
r.run();
} else {
pendingMain.add(r);
}
}
} else {
pendingTasks.add(task);
}
}
private static void continueTasks(boolean fromMain) {
for (Iterator<BootTask> it = pendingTasks.iterator(); it.hasNext();) {
BootTask task = it.next();
if (checkDependencies(task.dependencies)) {
tryRunTask(task, fromMain, true);
it.remove();
}
}
}
private static boolean checkDependencies(List<Class<?>> clzs) {
if (clzs.isEmpty()) {
return true;
}
for (int i = 0, s = tasks.size(); i < s; i++) {
if (clzs.contains(tasks.get(i).getClass())) {
if (!completed.get(i)) {
return false;
}
}
}
return true;
}
}
@@ -0,0 +1,25 @@
package ru.ytkab0bp.slicebeam.boot;
import org.json.JSONException;
import org.json.JSONObject;
import ru.ytkab0bp.slicebeam.BeamServerData;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
public class BeamServerDataTask extends BootTask {
public BeamServerDataTask() {
super(() -> {
try {
SliceBeam.SERVER_DATA = new BeamServerData(new JSONObject(Prefs.getBeamServerData()));
} catch (JSONException e) {
throw new RuntimeException(e);
}
if (System.currentTimeMillis() - Prefs.getLastCheckedInfo() >= 86400000L) {
ViewUtils.postOnMainThread(BeamServerData::load);
}
});
onWorker();
}
}
@@ -0,0 +1,34 @@
package ru.ytkab0bp.slicebeam.boot;
import java.util.Collections;
import java.util.List;
public class BootTask {
public final List<Class<?>> dependencies;
public final Runnable run;
public boolean workerThread;
public int priority;
public boolean nonCritical;
/* package */ int index;
public BootTask(Runnable run) {
this.dependencies = Collections.emptyList();
this.run = run;
}
public BootTask(List<Class<?>> dependencies, Runnable run) {
this.dependencies = dependencies;
this.run = run;
}
public BootTask onWorker() {
return onWorker(-20);
}
public BootTask onWorker(int priority) {
this.workerThread = true;
this.priority = priority;
return this;
}
}
@@ -0,0 +1,19 @@
package ru.ytkab0bp.slicebeam.boot;
import java.io.IOException;
import ru.ytkab0bp.slicebeam.SliceBeam;
public class CheckUpdateJsonTask extends BootTask {
public CheckUpdateJsonTask() {
super(() -> {
try {
SliceBeam.INSTANCE.getAssets().open("update.json").close();
SliceBeam.hasUpdateInfo = true;
} catch (IOException e) {
SliceBeam.hasUpdateInfo = false;
}
});
onWorker();
}
}
@@ -0,0 +1,21 @@
package ru.ytkab0bp.slicebeam.boot;
import java.io.File;
import ru.ytkab0bp.slicebeam.SliceBeam;
public class ClearModelCacheTask extends BootTask {
@SuppressWarnings("ResultOfMethodCallIgnored")
public ClearModelCacheTask() {
super(()->{
File cache = SliceBeam.getModelCacheDir();
if (cache.exists()) {
for (File f : cache.listFiles()) {
f.delete();
}
}
});
nonCritical = true;
onWorker();
}
}
@@ -0,0 +1,12 @@
package ru.ytkab0bp.slicebeam.boot;
import java.util.Collections;
import ru.ytkab0bp.slicebeam.cloud.CloudController;
public class CloudCachedInitTask extends BootTask {
public CloudCachedInitTask() {
super(Collections.singletonList(PrefsTask.class), CloudController::initCached);
onWorker();
}
}
@@ -0,0 +1,13 @@
package ru.ytkab0bp.slicebeam.boot;
import java.util.Arrays;
import ru.ytkab0bp.slicebeam.cloud.CloudController;
public class CloudInitTask extends BootTask {
public CloudInitTask() {
super(Arrays.asList(PrefsTask.class, TrueTimeTask.class, LoadSlic3rConfigTask.class, CloudCachedInitTask.class), CloudController::init);
onWorker();
nonCritical = true;
}
}
@@ -0,0 +1,12 @@
package ru.ytkab0bp.slicebeam.boot;
import ru.ytkab0bp.eventbus.EventBus;
import ru.ytkab0bp.slicebeam.BuildConfig;
public class EventBusTask extends BootTask {
public EventBusTask() {
super(() -> EventBus.registerImpl(BuildConfig.APPLICATION_ID));
onWorker();
}
}
@@ -0,0 +1,25 @@
package ru.ytkab0bp.slicebeam.boot;
import java.io.File;
import java.io.IOException;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
@SuppressWarnings("ResultOfMethodCallIgnored")
public class LoadSlic3rConfigTask extends BootTask {
public LoadSlic3rConfigTask() {
super(() -> {
File cfgFile = SliceBeam.getConfigFile();
SliceBeam.getCurrentConfigFile().delete();
if (cfgFile.exists()) {
try {
SliceBeam.CONFIG = new Slic3rConfigWrapper(cfgFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
onWorker();
}
}
@@ -0,0 +1,10 @@
package ru.ytkab0bp.slicebeam.boot;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.utils.Prefs;
public class PrefsTask extends BootTask {
public PrefsTask() {
super(()->Prefs.init(SliceBeam.INSTANCE));
}
}
@@ -0,0 +1,10 @@
package ru.ytkab0bp.slicebeam.boot;
import ru.ytkab0bp.slicebeam.slic3r.PrintConfigDef;
public class PrintConfigWarmupTask extends BootTask {
public PrintConfigWarmupTask() {
super(PrintConfigDef::getInstance);
onWorker();
}
}
@@ -0,0 +1,75 @@
package ru.ytkab0bp.slicebeam.boot;
import androidx.annotation.NonNull;
import com.instacart.truetime.TrueTimeEventListener;
import com.instacart.truetime.time.TrueTimeImpl;
import com.instacart.truetime.time.TrueTimeParameters;
import java.net.InetAddress;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import kotlinx.coroutines.Dispatchers;
import ru.ytkab0bp.slicebeam.SliceBeam;
public class TrueTimeTask extends BootTask {
public TrueTimeTask() {
super(() -> {
CountDownLatch latch = new CountDownLatch(1);
SliceBeam.TRUE_TIME = new TrueTimeImpl(new TrueTimeParameters.Builder().buildParams(), Dispatchers.getIO(), new TrueTimeEventListener() {
@Override
public void initialize(@NonNull TrueTimeParameters trueTimeParameters) {}
@Override
public void initializeSuccess(@NonNull long[] longs) {
latch.countDown();
}
@Override
public void initializeFailed(@NonNull Exception e) {}
@Override
public void nextInitializeIn(long l) {}
@Override
public void resolvedNtpHostToIPs(@NonNull String s, @NonNull List<? extends InetAddress> list) {}
@Override
public void lastSntpRequestAttempt(@NonNull InetAddress inetAddress) {}
@Override
public void sntpRequestFailed(@NonNull Exception e) {}
@Override
public void syncDispatcherException(@NonNull Throwable throwable) {}
@Override
public void sntpRequest(@NonNull InetAddress inetAddress) {}
@Override
public void sntpRequestSuccessful(@NonNull InetAddress inetAddress) {}
@Override
public void sntpRequestFailed(@NonNull InetAddress inetAddress, @NonNull Exception e) {}
@Override
public void storingTrueTime(@NonNull long[] longs) {}
@Override
public void returningTrueTime(@NonNull Date date) {}
@Override
public void returningDeviceTime() {}
});
SliceBeam.TRUE_TIME.sync();
try {
latch.await(300, TimeUnit.MILLISECONDS);
} catch (InterruptedException ignored) {}
});
onWorker();
nonCritical = true;
}
}
@@ -0,0 +1,12 @@
package ru.ytkab0bp.slicebeam.boot;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.utils.VibrationUtils;
public class VibrationUtilsTask extends BootTask {
public VibrationUtilsTask() {
super(() -> VibrationUtils.init(SliceBeam.INSTANCE));
onWorker();
}
}
@@ -0,0 +1,273 @@
package ru.ytkab0bp.slicebeam.cloud;
import androidx.annotation.Nullable;
import com.google.gson.Gson;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import ru.ytkab0bp.sapil.APICallback;
import ru.ytkab0bp.sapil.APILibrary;
import ru.ytkab0bp.sapil.APIRequestHandle;
import ru.ytkab0bp.sapil.APIRunner;
import ru.ytkab0bp.sapil.Arg;
import ru.ytkab0bp.sapil.Header;
import ru.ytkab0bp.sapil.Method;
import ru.ytkab0bp.sapil.RequestType;
import ru.ytkab0bp.slicebeam.BuildConfig;
import ru.ytkab0bp.slicebeam.utils.Prefs;
public interface CloudAPI extends APIRunner {
CloudAPI INSTANCE = APILibrary.newRunner(CloudAPI.class, new RunnerConfig() {
private final Map<String, String> headers = new HashMap<>();
@Override
public String getBaseURL() {
return "https://api.beam3d.ru/v1/";
}
@Override
public String getDefaultUserAgent() {
return "SliceBeam v" + BuildConfig.VERSION_NAME + "/" + BuildConfig.VERSION_CODE;
}
@Override
public Map<String, String> getDefaultHeaders() {
headers.clear();
if (Prefs.getCloudAPIToken() != null) {
headers.put("Authorization", "Bearer " + Prefs.getCloudAPIToken());
}
return headers;
}
});
/**
* Begins login flow, returns auth link
*/
@Method("login/begin")
APIRequestHandle loginBegin(APICallback<LoginData> callback);
/**
* Checks new login state by session id
*/
@Method("login/check")
void loginCheck(@Arg("sessionId") String sessionId, APICallback<LoginState> callback);
/**
* Cancels login flow
*/
@Method("login/cancel")
void loginCancel(@Arg("sessionId") String sessionId, APICallback<Boolean> callback);
/**
* Gets current user info
* <p>
* Requires authorization
*/
@Method("user/getInfo")
void userGetInfo(APICallback<UserInfo> callback);
/**
* Gets user features
*/
@Method("user/getFeatures")
void userGetFeatures(APICallback<UserFeatures> callback);
/**
* Fetches sync state
* <p>
* Requires authorization
*/
@Method("sync/getState")
void syncGetState(APICallback<SyncState> callback);
/**
* Uploads new data to the server
* <p>
* @param data New base64 encoded data
* <p>
* Requires authorization
*/
@Method(requestType = RequestType.POST, value = "sync/upload")
void syncUpload(@Arg("") String data, @Header("Content-Type") String type, APICallback<SyncState> callback);
/**
* Downloads base64 data
* <p>
* Requires authorization
*/
@Method("sync/get")
void syncGet(APICallback<String> callback);
/**
* Generates 3D model from image
* <p>
* @param image Base64 encoded image
* <p>
* Requires authorization
*/
@Method(requestType = RequestType.POST, value = "models/generate")
void modelsGenerate(@Arg("") String image, @Header("Content-Type") String type, APICallback<InputStream> callback);
/**
* Gets remaining model generations count
* <p>
* Requires authorization
*/
@Method("models/getRemainingCount")
void modelsGetRemainingCount(APICallback<ModelsRemainingCount> callback);
/**
* Destroys token
* <p>
* Requires authorization
*/
@Method("logout")
void logout(APICallback<Boolean> callback);
final class LoginData {
/**
* Url that should be clicked by the user to authorize
*/
public String url;
/**
* Session identifier
*/
public String sessionId;
/**
* Time at which session should be considered expired if not logged in
*/
public long expiresAt;
}
final class LoginState {
/**
* If user is now logged in
*/
public boolean loggedIn;
/**
* Bearer token if auth was successful
*/
public String bearer;
}
final class UserFeatures {
/**
* Which level is required for early access
*/
public int earlyAccessLevel;
/**
* Which level is required for data sync
*/
public int syncRequiredLevel;
/**
* Which level is required for AI model generator
*/
public int aiGeneratorRequiredLevel;
/**
* Models per month max
*/
public int aiGeneratorModelsPerMonth;
/**
* Url at which user should be redirected for info about how to restore a subscription
*/
public String alreadySubscribedInfoUrl;
/**
* List of subscription levels
*/
public List<SubscriptionLevel> levels = new ArrayList<>();
}
final class SubscriptionLevel {
/**
* Int representation
*/
public int level;
/**
* Title of this level
*/
public String title;
/**
* Price of this level
*/
public String price;
/**
* Url at which user should be redirected for purchase
*/
public String subscribeOrUpgradeUrl;
/**
* Url at which user should be redirected for managing the subscription
*/
public String manageUrl;
}
final class UserInfo {
/**
* User's id
*/
public String id;
/**
* User's display name
*/
public String displayName;
/**
* User's avatar. Could be null
*/
@Nullable
public String avatarUrl;
/**
* Current subscription level
*/
public int currentLevel;
}
final class SyncState {
/**
* Cloud data last updated time
*/
public long lastUpdatedDate = 0;
/**
* Used size of cloud storage
*/
public long usedSize;
/**
* Max storage size
*/
public long maxSize;
}
final class ModelsRemainingCount {
/**
* Used generations
*/
public int used;
/**
* Max available generations
*/
public int max;
}
}
@@ -0,0 +1,449 @@
package ru.ytkab0bp.slicebeam.cloud;
import android.content.Intent;
import android.net.Uri;
import android.util.Base64;
import android.util.Log;
import com.google.gson.Gson;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import ru.ytkab0bp.sapil.APICallback;
import ru.ytkab0bp.sapil.APIRequestHandle;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.events.CloudFeaturesUpdatedEvent;
import ru.ytkab0bp.slicebeam.events.CloudLoginStateUpdatedEvent;
import ru.ytkab0bp.slicebeam.events.CloudModelsRemainingCountUpdatedEvent;
import ru.ytkab0bp.slicebeam.events.CloudSyncFinishedEvent;
import ru.ytkab0bp.slicebeam.events.CloudUserInfoUpdatedEvent;
import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
import ru.ytkab0bp.slicebeam.utils.IOUtils;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
public class CloudController {
public final static String USER_INFO_AI_GEN_TAG = "ai_gen_user_info";
public final static String CLOUD_SYNC_TAG = "cloud_sync";
private final static String TAG = "cloud";
private final static long MIN_SYNC_DELTA = 5 * 60 * 1000L; // Once in 5 minutes
private final static long MIN_SYNC_FEATURES_DELTA = 12 * 60 * 60 * 1000L; // Once in 12 hours
private static boolean isSyncInProgress;
private static CloudAPI.UserInfo userInfo;
private static CloudAPI.UserFeatures userFeatures;
private static int modelsUsed;
private static int modelsMaxGenerations;
private static boolean isLoggingIn;
private static APIRequestHandle beginLoginHandle;
private static String loginSessionId;
private static Runnable loginAutoCancel = () -> {
loginSessionId = null;
isLoggingIn = false;
SliceBeam.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
};
private static Runnable loginCheck = new Runnable() {
@Override
public void run() {
CloudAPI.INSTANCE.loginCheck(loginSessionId, new APICallback<CloudAPI.LoginState>() {
@Override
public void onResponse(CloudAPI.LoginState response) {
if (response.loggedIn) {
Prefs.setCloudAPIToken(response.bearer);
loadUserInfo();
ViewUtils.removeCallbacks(loginAutoCancel);
} else if (isLoggingIn) {
ViewUtils.postOnMainThread(loginCheck, 5000);
}
}
@Override
public void onException(Exception e) {
Log.e(TAG, "Failed to check login state", e);
if (isLoggingIn) {
ViewUtils.postOnMainThread(loginCheck, 5000);
}
}
});
}
};
private static Gson gson = new Gson();
public static void initCached() {
if (Prefs.getCloudCachedUserFeatures() != null) {
userFeatures = gson.fromJson(Prefs.getCloudCachedUserFeatures(), CloudAPI.UserFeatures.class);
}
if (Prefs.getCloudAPIToken() != null) {
if (Prefs.getCloudCachedUserInfo() != null) {
userInfo = gson.fromJson(Prefs.getCloudCachedUserInfo(), CloudAPI.UserInfo.class);
modelsUsed = Prefs.getCloudCachedUsedModels();
modelsMaxGenerations = Prefs.getCloudCachedMaxModels();
}
}
}
public static void init() {
long now = SliceBeam.TRUE_TIME.now().getTime();
boolean needSyncInfo = userFeatures == null || now - Prefs.getCloudLastFeaturesSync() > MIN_SYNC_FEATURES_DELTA;
if (needSyncInfo) {
checkUserFeatures();
}
if (Prefs.getCloudAPIToken() != null) {
if (needSyncInfo || userInfo == null) {
loadUserInfo();
}
if (!needSyncInfo && userInfo != null && isSyncAvailable() && Prefs.isCloudProfileSyncEnabled()) {
if (now - Prefs.getCloudLastSync() > MIN_SYNC_DELTA) {
syncData();
}
}
}
}
private static void loadUserInfo() {
CloudAPI.INSTANCE.userGetInfo(new APICallback<CloudAPI.UserInfo>() {
@Override
public void onResponse(CloudAPI.UserInfo response) {
userInfo = response;
if (userInfo.id.equals("null")) {
userInfo = null;
Prefs.setCloudAPIToken(null);
Prefs.setCloudCachedUserInfo(null);
SliceBeam.EVENT_BUS.fireEvent(new CloudUserInfoUpdatedEvent());
if (isLoggingIn) {
isLoggingIn = false;
SliceBeam.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
}
} else {
Prefs.setCloudCachedUserInfo(gson.toJson(userInfo));
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(USER_INFO_AI_GEN_TAG));
SliceBeam.EVENT_BUS.fireEvent(new CloudUserInfoUpdatedEvent());
if (isLoggingIn) {
isLoggingIn = false;
SliceBeam.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
}
if (isSyncAvailable() && Prefs.isCloudProfileSyncEnabled()) {
syncData();
}
checkGeneratorRemaining();
}
Prefs.setCloudLastFeaturesSync(SliceBeam.TRUE_TIME.now().getTime());
}
@Override
public void onException(Exception e) {
Log.e(TAG, "Failed to get user info", e);
ViewUtils.postOnMainThread(CloudController::init, 15000);
}
});
}
public static boolean isLoggingIn() {
return isLoggingIn;
}
private static void beginLogin0() {
beginLoginHandle = CloudAPI.INSTANCE.loginBegin(new APICallback<CloudAPI.LoginData>() {
@Override
public void onResponse(CloudAPI.LoginData response) {
loginSessionId = response.sessionId;
ViewUtils.postOnMainThread(loginAutoCancel, response.expiresAt * 1000L - SliceBeam.TRUE_TIME.now().getTime());
ViewUtils.postOnMainThread(loginCheck, 5000);
ViewUtils.postOnMainThread(() -> SliceBeam.INSTANCE.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(response.url)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)));
}
@Override
public void onException(Exception e) {
ViewUtils.postOnMainThread(CloudController::beginLogin0, 15000);
}
});
}
public static void beginLogin() {
isLoggingIn = true;
SliceBeam.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
beginLogin0();
}
public static void cancelLogin() {
isLoggingIn = false;
SliceBeam.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
if (loginSessionId != null) {
CloudAPI.INSTANCE.loginCancel(loginSessionId, response -> {});
}
if (beginLoginHandle != null && beginLoginHandle.isRunning()) {
beginLoginHandle.cancel();
beginLoginHandle = null;
}
ViewUtils.removeCallbacks(loginCheck);
ViewUtils.removeCallbacks(loginAutoCancel);
loginSessionId = null;
}
public static void logout() {
Prefs.setCloudAPIToken(null);
userInfo = null;
SliceBeam.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
SliceBeam.EVENT_BUS.fireEvent(new CloudUserInfoUpdatedEvent());
CloudAPI.INSTANCE.logout(response -> {});
}
public static void checkGeneratorRemaining() {
CloudAPI.INSTANCE.modelsGetRemainingCount(new APICallback<CloudAPI.ModelsRemainingCount>() {
@Override
public void onResponse(CloudAPI.ModelsRemainingCount response) {
modelsUsed = response.used;
modelsMaxGenerations = response.max;
Prefs.setCloudCachedUsedMaxModels(modelsUsed, modelsMaxGenerations);
SliceBeam.EVENT_BUS.fireEvent(new CloudModelsRemainingCountUpdatedEvent());
}
@Override
public void onException(Exception e) {
Log.e(TAG, "Failed to check remaining models", e);
ViewUtils.postOnMainThread(CloudController::checkGeneratorRemaining, 15000);
}
});
}
public static void checkUserFeatures() {
CloudAPI.INSTANCE.userGetFeatures(new APICallback<CloudAPI.UserFeatures>() {
@Override
public void onResponse(CloudAPI.UserFeatures response) {
userFeatures = response;
Prefs.setCloudCachedUserFeatures(gson.toJson(userFeatures));
if (Prefs.getCloudAPIToken() == null) {
Prefs.setCloudLastFeaturesSync(SliceBeam.TRUE_TIME.now().getTime());
}
SliceBeam.EVENT_BUS.fireEvent(new CloudFeaturesUpdatedEvent());
}
@Override
public void onException(Exception e) {
Log.e(TAG, "Failed to get user features", e);
ViewUtils.postOnMainThread(CloudController::checkUserFeatures, 15000);
}
});
}
public static CloudAPI.UserInfo getUserInfo() {
return userInfo;
}
public static CloudAPI.UserFeatures getUserFeatures() {
return userFeatures;
}
public static boolean hasAccountFeatures() {
return userFeatures != null && userFeatures.levels != null && !userFeatures.levels.isEmpty();
}
public static boolean isSyncAvailable() {
return Prefs.getCloudAPIToken() != null && userInfo != null && userFeatures != null && userInfo.currentLevel >= userFeatures.syncRequiredLevel;
}
public static boolean needShowAIGenerator() {
return userFeatures != null && userFeatures.aiGeneratorRequiredLevel >= 0;
}
public static int getGeneratedModels() {
return modelsUsed;
}
public static int getMaxGeneratedModels() {
return modelsMaxGenerations;
}
private static void downloadData(long lastModified) {
CloudAPI.INSTANCE.syncGet(new APICallback<String>() {
@Override
public void onResponse(String response) {
IOUtils.IO_POOL.submit(() -> {
try {
File f = SliceBeam.getConfigFile();
byte[] data = Base64.decode(response, 0);
FileOutputStream fos = new FileOutputStream(f);
fos.write(data);
fos.close();
SliceBeam.CONFIG = new Slic3rConfigWrapper(f);
Prefs.setCloudLocalLastModified(lastModified);
Prefs.setCloudLocalLastSentModified(lastModified);
Prefs.setCloudRemoteLastModified(lastModified);
Prefs.setCloudLastSync(SliceBeam.TRUE_TIME.now().getTime());
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.CloudSyncSuccess));
SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
} catch (IOException e) {
Log.e(TAG, "Failed to write data", e);
isSyncInProgress = false;
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
}
});
}
@Override
public void onException(Exception e) {
Log.e(TAG, "Failed to download data", e);
isSyncInProgress = false;
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
}
});
}
private static void syncData() {
if (isSyncInProgress) {
return;
}
long modified = Prefs.getCloudLocalLastModified();
isSyncInProgress = true;
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.CloudSyncInProgress).tag(CLOUD_SYNC_TAG));
CloudAPI.INSTANCE.syncGetState(new APICallback<CloudAPI.SyncState>() {
@Override
public void onResponse(CloudAPI.SyncState response) {
if (SliceBeam.CONFIG == null && response.usedSize != 0) {
// Setup screen, no config yet
downloadData(response.lastUpdatedDate);
} else if (response.usedSize == 0) {
if (SliceBeam.CONFIG == null) {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
return;
}
// No data on server yet, send anyway
uploadData(modified);
} else if (response.lastUpdatedDate != Prefs.getCloudRemoteLastModified()) {
if (Prefs.getCloudLocalLastSentModified() == modified) {
// Modified only on server
downloadData(response.lastUpdatedDate);
} else {
// Modified on client and on server
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.WARNING, R.string.CloudSyncConflict).button(R.string.CloudSyncConflictResolve, v -> {
SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy HH:mm", Locale.getDefault());
new BeamAlertDialogBuilder(v.getContext())
.setTitle(R.string.CloudSyncConflict)
.setMessage(v.getContext().getString(R.string.CloudSyncConflictResolveMessage, format.format(new Date(response.lastUpdatedDate)), format.format(new Date(Prefs.getCloudLocalLastModified()))))
.setPositiveButton(R.string.CloudSyncConflictChooseRemote, (dialog, which) -> downloadData(response.lastUpdatedDate))
.setNegativeButton(R.string.CloudSyncConflictChooseLocal, (dialog, which) -> uploadData(modified))
.show();
}).tag(CLOUD_SYNC_TAG));
}
} else {
if (Prefs.getCloudLocalLastSentModified() != modified) {
// Modified only on client
uploadData(modified);
} else {
// Not modified on server and on client
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
}
}
}
@Override
public void onException(Exception e) {
Log.e(TAG, "Failed to get sync state", e);
isSyncInProgress = false;
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
}
});
}
private static void uploadData(long modified) {
IOUtils.IO_POOL.submit(() -> {
try {
File f = SliceBeam.getConfigFile();
FileInputStream fis = new FileInputStream(f);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[10240];
int c;
while ((c = fis.read(buffer)) != -1) {
bos.write(buffer, 0, c);
}
bos.close();
fis.close();
CloudAPI.INSTANCE.syncUpload(Base64.encodeToString(bos.toByteArray(), Base64.NO_WRAP), "application/ini", new APICallback<CloudAPI.SyncState>() {
@Override
public void onResponse(CloudAPI.SyncState response) {
isSyncInProgress = false;
if (Prefs.getCloudLocalLastModified() != modified) { // Re-send otherwise
syncData();
return;
}
Prefs.setCloudRemoteLastModified(response.lastUpdatedDate);
Prefs.setCloudLocalLastSentModified(modified);
Prefs.setCloudLastSync(SliceBeam.TRUE_TIME.now().getTime());
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.CloudSyncSuccess));
SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
}
@Override
public void onException(Exception e) {
Log.e(TAG, "Failed to upload sync data", e);
isSyncInProgress = false;
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
}
});
} catch (IOException e) {
Log.e(TAG, "Failed to read sync data", e);
isSyncInProgress = false;
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
}
});
}
public static void notifyDataChanged() {
long now = SliceBeam.TRUE_TIME.now().getTime();
Prefs.setCloudLocalLastModified(now);
if (!isSyncAvailable() || !Prefs.isCloudProfileSyncEnabled()) {
return;
}
if (now - Prefs.getCloudLastSync() > MIN_SYNC_DELTA) {
syncData();
}
}
}
@@ -0,0 +1,158 @@
package ru.ytkab0bp.slicebeam.components;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.net.Uri;
import android.text.SpannableStringBuilder;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.Space;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import java.util.ArrayList;
import java.util.List;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.cloud.CloudAPI;
import ru.ytkab0bp.slicebeam.cloud.CloudController;
import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
import ru.ytkab0bp.slicebeam.recycler.PreferenceSwitchItem;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerAdapter;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.TextColorImageSpan;
public class CloudManageBottomSheet extends BottomSheetDialog {
public CloudManageBottomSheet(@NonNull Context context) {
super(context);
LinearLayout ll = new LinearLayout(context);
ll.setOrientation(LinearLayout.VERTICAL);
GradientDrawable gd = new GradientDrawable();
gd.setCornerRadii(new float[] {
ViewUtils.dp(28), ViewUtils.dp(28),
ViewUtils.dp(28), ViewUtils.dp(28),
0, 0,
0, 0
});
gd.setColor(ThemesRepo.getColor(R.attr.dialogBackground));
ll.setBackground(gd);
ll.setPadding(0, ViewUtils.dp(12), 0, ViewUtils.dp(12));
TextView title = new TextView(context);
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20);
title.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
title.setText(R.string.SettingsCloudManageButtonManage);
title.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
title.setGravity(Gravity.CENTER);
title.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
leftMargin = rightMargin = ViewUtils.dp(21);
}});
ll.addView(title);
TextView description = new TextView(context);
description.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
description.setText(context.getString(R.string.SettingsCloudManageLoggedInAs, CloudController.getUserInfo().displayName));
description.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
description.setGravity(Gravity.CENTER);
description.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
leftMargin = rightMargin = ViewUtils.dp(21);
topMargin = ViewUtils.dp(8);
}});
ll.addView(description);
int currentLevel = CloudController.getUserInfo().currentLevel;
CloudAPI.SubscriptionLevel lvl = null;
CloudAPI.UserFeatures features = CloudController.getUserFeatures();
for (CloudAPI.SubscriptionLevel level : features.levels) {
if (level.level != -1 && level.level <= currentLevel && (lvl == null || level.level > lvl.level)) {
lvl = level;
}
}
if (lvl != null) {
List<SimpleRecyclerItem> items = new ArrayList<>();
if (currentLevel >= features.syncRequiredLevel) {
items.add(new PreferenceSwitchItem()
.setIcon(R.drawable.sync_outline_28)
.setTitle(context.getString(R.string.SettingsCloudManageFeatureCloudSync))
.setValueProvider(Prefs::isCloudProfileSyncEnabled)
.setChangeListener((buttonView, isChecked) -> {
Prefs.setCloudProfileSyncEnabled(isChecked);
if (isChecked) {
CloudController.notifyDataChanged();
} else {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CloudController.CLOUD_SYNC_TAG));
}
}));
}
if (!items.isEmpty()) {
RecyclerView recyclerView = new RecyclerView(context);
recyclerView.setLayoutManager(new LinearLayoutManager(context));
recyclerView.setBackground(ViewUtils.createRipple(0, ColorUtils.setAlphaComponent(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 0x10), 16));
SimpleRecyclerAdapter adapter = new SimpleRecyclerAdapter();
adapter.setItems(items);
recyclerView.setAdapter(adapter);
ll.addView(recyclerView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
topMargin = ViewUtils.dp(16);
leftMargin = rightMargin = ViewUtils.dp(16);
}});
}
TextView manageButton = new TextView(context);
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(context.getString(R.string.SettingsCloudManageSubscription)).append(" ");
Drawable dr = ContextCompat.getDrawable(context, R.drawable.external_link_outline_24);
int size = ViewUtils.dp(16);
dr.setBounds(0, 0, size, size);
sb.append("d", new TextColorImageSpan(dr, ViewUtils.dp(2f)), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
manageButton.setText(sb);
manageButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
manageButton.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
manageButton.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
manageButton.setGravity(Gravity.CENTER);
manageButton.setPadding(ViewUtils.dp(12), ViewUtils.dp(8), ViewUtils.dp(12), ViewUtils.dp(8));
manageButton.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
CloudAPI.SubscriptionLevel finalLvl = lvl;
manageButton.setOnClickListener(v -> v.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(finalLvl.manageUrl))));
ll.addView(manageButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(48)) {{
leftMargin = rightMargin = ViewUtils.dp(16);
topMargin = bottomMargin = ViewUtils.dp(6);
}});
} else {
ll.addView(new Space(context), new LinearLayout.LayoutParams(0, ViewUtils.dp(16)));
}
TextView buttonView = new TextView(context);
buttonView.setText(R.string.SettingsCloudManageButtonLogOut);
buttonView.setTextColor(ThemesRepo.getColor(R.attr.textColorOnAccent));
buttonView.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
buttonView.setGravity(Gravity.CENTER);
buttonView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
buttonView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), ThemesRepo.getColor(R.attr.textColorNegative), 16));
buttonView.setOnClickListener(v-> {
CloudController.logout();
dismiss();
});
ll.addView(buttonView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)) {{
leftMargin = rightMargin = ViewUtils.dp(16);
bottomMargin = ViewUtils.dp(4);
}});
setContentView(ll);
}
}
@@ -204,10 +204,13 @@ public abstract class UnfoldMenu {
}
fragment.getGlView().invalidate();
}
});
})
.addEndListener((animation, canceled, value, velocity) -> onShown());
spring.start();
}
protected void onShown() {}
public void relayout() {
FrameLayout into = containerLayout;
boolean portrait = into.getWidth() < into.getHeight();
@@ -5,6 +5,10 @@ import android.content.res.ColorStateList;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.Gravity;
@@ -16,16 +20,23 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.ColorUtils;
import androidx.dynamicanimation.animation.FloatValueHolder;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import ru.ytkab0bp.slicebeam.theme.IThemeView;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.RandomUtils;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolderView> {
@@ -36,6 +47,7 @@ public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolde
public boolean isEnabled = true;
public boolean isChecked = false;
public boolean isCheckable = false;
public boolean isShiny = false;
public View.OnClickListener clickListener;
public CompoundButton.OnCheckedChangeListener checkedChangeListener;
@@ -61,6 +73,11 @@ public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolde
return this;
}
public BedMenuItem setShiny(boolean shiny) {
isShiny = shiny;
return this;
}
public BedMenuItem setSingleLine(boolean singleLine) {
isSingleLine = singleLine;
return this;
@@ -77,6 +94,9 @@ public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolde
}
public final static class BedMenuItemHolderView extends LinearLayout implements IThemeView {
private final static float IN_BOUND = 0.05f;
private final static float OUT_BOUND = 0.1f;
private ImageView icon;
private TextView title;
@@ -84,7 +104,13 @@ public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolde
private Paint bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Path path = new Path();
private Path path2 = new Path();
private float checkedProgress;
private boolean enabled;
private boolean shiny;
private List<Sparkle> sparkles;
private long lastDraw;
private Drawable sparkleDrawable;
public BedMenuItemHolderView(Context context) {
super(context);
@@ -108,16 +134,21 @@ public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolde
setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT) {{
leftMargin = topMargin = bottomMargin = ViewUtils.dp(6);
}});
setClipToPadding(false);
setClipChildren(false);
setWillNotDraw(false);
onApplyTheme();
}
@Override
public void draw(@NonNull Canvas canvas) {
long dt = Math.min(System.currentTimeMillis() - lastDraw, 16);
lastDraw = System.currentTimeMillis();
int rad = ViewUtils.dp(16);
canvas.drawRoundRect(0, 0, getWidth(), getHeight(), rad, rad, bgPaint);
if (checkedProgress != 0f) {
if (enabled && checkedProgress != 0f) {
if (checkedProgress == 1f) {
canvas.drawRoundRect(0, 0, getWidth(), getHeight(), rad, rad, accentPaint);
} else {
@@ -132,6 +163,61 @@ public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolde
}
super.draw(canvas);
if (shiny) {
float side = Math.min(getWidth(), getHeight());
canvas.save();
if (sparkles == null) sparkles = new ArrayList<>();
if (sparkleDrawable == null) {
sparkleDrawable = ContextCompat.getDrawable(SliceBeam.INSTANCE, R.drawable.sparkle_28);
sparkleDrawable.setColorFilter(new PorterDuffColorFilter(ThemesRepo.getColor(android.R.attr.colorAccent), PorterDuff.Mode.SRC_IN));
}
float p = dt / 1000f;
for (Iterator<Sparkle> iterator = sparkles.iterator(); iterator.hasNext(); ) {
Sparkle sparkle = iterator.next();
sparkle.position.x += sparkle.velocity.x * p;
sparkle.position.y += sparkle.velocity.y * p;
sparkle.velocity.x *= 0.9999f;
sparkle.velocity.y *= 0.9999f;
sparkle.living += dt;
int size = (int) (side * sparkle.size);
float fadems = 200;
if ((sparkle.position.x - sparkle.size > 0 && sparkle.position.x + sparkle.size < 1f) &&
sparkle.lifetime - sparkle.living > fadems) {
sparkle.living = (long) (sparkle.lifetime - fadems);
}
if (sparkle.living >= sparkle.lifetime) {
iterator.remove();
} else {
float alpha = sparkle.living < fadems ? sparkle.living / fadems : sparkle.living > sparkle.lifetime - fadems ? (sparkle.lifetime - sparkle.living) / fadems : 1f;
canvas.saveLayerAlpha(-OUT_BOUND * side, -OUT_BOUND * side, getWidth() + OUT_BOUND * side, getHeight() + OUT_BOUND * side, (int) (alpha * sparkle.alpha * 0xFF));
canvas.translate(sparkle.position.x * side, sparkle.position.y * side);
sparkleDrawable.setBounds(-size / 2, -size / 2, size / 2, size / 2);
sparkleDrawable.draw(canvas);
canvas.restore();
}
}
if (sparkles.size() < 20) {
int s = 20 - sparkles.size();
for (int i = 0; i < s; i++) {
if (RandomUtils.RANDOM.nextFloat() < 0.01f) {
Sparkle sparkle = new Sparkle();
boolean leftSide = RandomUtils.RANDOM.nextBoolean();
sparkle.position = new PointF(leftSide ? RandomUtils.randomf(-OUT_BOUND, 0) : RandomUtils.randomf(1, 1 + OUT_BOUND), RandomUtils.randomf(-OUT_BOUND, 1 + OUT_BOUND));
sparkle.velocity = new PointF(RandomUtils.randomf(-0.05f, 0.05f), RandomUtils.randomf(-0.05f, 0.05f));
sparkle.size = RandomUtils.randomf(0.1f, 0.12f);
sparkle.alpha = RandomUtils.randomf(0.5f, 1f);
sparkle.lifetime = RandomUtils.randoml(4000, 10000);
sparkles.add(sparkle);
}
}
}
invalidate();
canvas.restore();
}
}
@Override
@@ -144,10 +230,13 @@ public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolde
}
public void bind(BedMenuItem item) {
enabled = item.isEnabled;
shiny = item.isShiny;
title.setMaxLines(item.isSingleLine ? 1 : 2);
title.setText(item.titleRes);
icon.setImageResource(item.iconRes);
checkedProgress = item.isCheckable && item.isChecked ? 1 : 0;
onApplyTheme();
title.setTextColor(ColorUtils.blendARGB(ThemesRepo.getColor(android.R.attr.textColorPrimary), ThemesRepo.getColor(R.attr.textColorOnAccent), checkedProgress));
icon.setImageTintList(ColorStateList.valueOf(ColorUtils.blendARGB(ThemesRepo.getColor(android.R.attr.textColorSecondary), ThemesRepo.getColor(R.attr.textColorOnAccent), checkedProgress)));
@@ -185,5 +274,14 @@ public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolde
bgPaint.setColor(ColorUtils.setAlphaComponent(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 0x10));
accentPaint.setColor(ThemesRepo.getColor(android.R.attr.colorAccent));
}
private final static class Sparkle {
private PointF position;
private PointF velocity;
private float size;
private float alpha;
private long lifetime;
private long living;
}
}
}
@@ -1,5 +1,6 @@
package ru.ytkab0bp.slicebeam.components.bed_menu;
import android.content.Context;
import android.widget.Toast;
import androidx.dynamicanimation.animation.FloatValueHolder;
@@ -10,6 +11,7 @@ import java.util.Arrays;
import java.util.List;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import ru.ytkab0bp.slicebeam.recycler.SpaceItem;
import ru.ytkab0bp.slicebeam.render.Camera;
@@ -122,7 +124,20 @@ public class CameraMenu extends ListBedMenu {
animateTo(toOrigin, toPosition);
}),
new SpaceItem(portrait ? ViewUtils.dp(8) : 0, portrait ? 0 : ViewUtils.dp(8)),
new BedMenuItem(R.string.MenuCameraEnableRotation, R.drawable.sync_outline_28).setCheckable((buttonView, isChecked) -> Prefs.setRotationEnabled(isChecked), Prefs.isRotationEnabled()),
new BedMenuItem(R.string.MenuCameraControlMode, R.drawable.rectangle_hand_point_up_28).onClick(v -> {
Context ctx = v.getContext();
new BeamAlertDialogBuilder(v.getContext())
.setTitle(R.string.MenuCameraControlModeFull)
.setSingleChoiceItems(new CharSequence[] {
ctx.getString(R.string.MenuCameraControlModeOne),
ctx.getString(R.string.MenuCameraControlModeTwo),
ctx.getString(R.string.MenuCameraControlModeThree)
}, Prefs.getCameraControlMode(), (dialog, which) -> {
Prefs.setCameraControlMode(which);
dialog.dismiss();
})
.show();
}),
new BedMenuItem(R.string.MenuCameraOrtho, R.drawable.image_format_32).setCheckable((buttonView, isChecked) -> {
Prefs.setOrthoProjectionEnabled(isChecked);
fragment.getGlView().getRenderer().updateProjection();
@@ -4,6 +4,7 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.provider.MediaStore;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
@@ -14,12 +15,14 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.core.content.FileProvider;
import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.RecyclerView;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@@ -30,14 +33,22 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import ru.ytkab0bp.eventbus.EventHandler;
import ru.ytkab0bp.slicebeam.BeamServerData;
import ru.ytkab0bp.slicebeam.BuildConfig;
import ru.ytkab0bp.slicebeam.MainActivity;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SetupActivity;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.cloud.CloudController;
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
import ru.ytkab0bp.slicebeam.components.WebViewMenu;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.events.CloudFeaturesUpdatedEvent;
import ru.ytkab0bp.slicebeam.events.CloudModelsRemainingCountUpdatedEvent;
import ru.ytkab0bp.slicebeam.events.NeedDismissAIGeneratorMenu;
import ru.ytkab0bp.slicebeam.events.NeedDismissCalibrationsMenu;
import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.ObjectsListChangedEvent;
import ru.ytkab0bp.slicebeam.events.SelectedObjectChangedEvent;
@@ -50,13 +61,18 @@ import ru.ytkab0bp.slicebeam.slic3r.Bed3D;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rRuntimeError;
import ru.ytkab0bp.slicebeam.theme.BeamTheme;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.DividerView;
import ru.ytkab0bp.slicebeam.view.FadeRecyclerView;
import ru.ytkab0bp.slicebeam.view.SegmentsView;
import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
public class FileMenu extends ListBedMenu {
private final static List<String> K3D_SUPPORTED_LANGUAGES = Arrays.asList("en", "ru");
private boolean wasPortrait;
private String getK3DLanguage() {
String lang = Locale.getDefault().getLanguage();
return K3D_SUPPORTED_LANGUAGES.contains(lang) ? lang : "en";
@@ -74,13 +90,18 @@ public class FileMenu extends ListBedMenu {
.replace("\"", "\\\"");
}
private boolean hasModel() {
return fragment.getGlView().getRenderer().getModel() != null;
}
private boolean hasSelection() {
return fragment.getGlView().getRenderer().getModel() != null && fragment.getGlView().getRenderer().getSelectedObject() != -1;
return hasModel() && fragment.getGlView().getRenderer().getSelectedObject() != -1;
}
@Override
protected List<SimpleRecyclerItem> onCreateItems(boolean portrait) {
return Arrays.asList(
wasPortrait = portrait;
List<SimpleRecyclerItem> list = new ArrayList<>(Arrays.asList(
new BedMenuItem(R.string.MenuFileOpen, R.drawable.folder_simple_plus_outline_28).onClick(v -> {
if (!fragment.getGlView().getRenderer().getBed().isValid()) {
Toast.makeText(fragment.getContext(), R.string.BedConfigurationError, Toast.LENGTH_SHORT).show();
@@ -104,7 +125,31 @@ public class FileMenu extends ListBedMenu {
fragment.updateModel();
}
}),
new SpaceItem(portrait ? ViewUtils.dp(3) : 0, portrait ? 0 : ViewUtils.dp(3)),
new SpaceItem(portrait ? ViewUtils.dp(3) : 0, portrait ? 0 : ViewUtils.dp(3))));
if (BeamServerData.isBoostyAvailable() && CloudController.needShowAIGenerator()) {
list.add(new BedMenuItem(R.string.MenuFileAIGenerator, R.drawable.picture_stack_outline_28).setShiny(true).onClick(view -> {
if (Prefs.getCloudAPIToken() == null || CloudController.getUserInfo() != null && CloudController.getMaxGeneratedModels() == 0) {
Context ctx = view.getContext();
ctx.startActivity(new Intent(ctx, SetupActivity.class).putExtra(SetupActivity.EXTRA_CLOUD_PROFILE, true));
return;
}
if (CloudController.getUserInfo() == null) {
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuFileAIGeneratorPleaseWaitSetup).tag(CloudController.USER_INFO_AI_GEN_TAG));
ViewUtils.postOnMainThread(() -> {
if (CloudController.getUserInfo() == null) {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CloudController.USER_INFO_AI_GEN_TAG));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.MenuFileAIGeneratorErrorNotLoadedUserAccount));
} else {
fragment.showUnfoldMenu(new AIGeneratorMenu(), view);
}
}, 2500);
return;
}
fragment.showUnfoldMenu(new AIGeneratorMenu(), view);
}));
}
list.addAll(Arrays.asList(
new BedMenuItem(R.string.MenuFileCalibrations, R.drawable.wrench_outline_28).setSingleLine(true).onClick(v -> {
if (!fragment.getGlView().getRenderer().getBed().isValid()) {
Toast.makeText(fragment.getContext(), R.string.BedConfigurationError, Toast.LENGTH_SHORT).show();
@@ -200,14 +245,28 @@ public class FileMenu extends ListBedMenu {
.show())
.setNegativeButton(android.R.string.cancel, null)
.show();
}),
new BedMenuItem(R.string.MenuFileExport3mf, R.drawable.arrow_down_to_square_outline_28).setEnabled(hasModel()).onClick(v -> {
if (fragment.getContext() instanceof Activity) {
Activity act = (Activity) fragment.getContext();
Intent i = new Intent(Intent.ACTION_CREATE_DOCUMENT);
i.setType("application/3mf");
i.putExtra(Intent.EXTRA_TITLE, "SliceBeam_project.3mf");
act.startActivityForResult(i, MainActivity.REQUEST_CODE_EXPORT_3MF);
}
})
);
));
return list;
}
@EventHandler(runOnMainThread = true)
public void onObjectsChanged(ObjectsListChangedEvent e) {
((BedMenuItem) adapter.getItems().get(1)).setEnabled(hasSelection());
adapter.notifyItemChanged(1);
int i = 8 - (BeamServerData.isBoostyAvailable() && CloudController.needShowAIGenerator() ? 0 : 1);
((BedMenuItem) adapter.getItems().get(i)).setEnabled(hasModel());
adapter.notifyItemChanged(i);
}
@EventHandler(runOnMainThread = true)
@@ -216,8 +275,152 @@ public class FileMenu extends ListBedMenu {
adapter.notifyItemChanged(1);
}
public final class CalibrationsMenu extends UnfoldMenu {
@EventHandler(runOnMainThread = true)
public void onFeaturedUpdated(CloudFeaturesUpdatedEvent e) {
adapter.setItems(onCreateItems(wasPortrait));
}
public final static class AIGeneratorMenu extends UnfoldMenu {
private TextView remainingView;
private SegmentsView segmentsView;
@Override
public int getRequestedSize(FrameLayout into, boolean portrait) {
return (int) (portrait ? ViewUtils.dp(52) + ViewUtils.dp(60) * 2 + ViewUtils.dp(28) + ViewUtils.dp(18) + ViewUtils.dp(2) : into.getWidth() * 0.6f);
}
@Override
protected View onCreateView(Context ctx, boolean portrait) {
LinearLayout ll = new LinearLayout(ctx);
ll.setOrientation(LinearLayout.VERTICAL);
RecyclerView rv = new FadeRecyclerView(ctx);
rv.setOverScrollMode(View.OVER_SCROLL_NEVER);
SimpleRecyclerAdapter adapter = new SimpleRecyclerAdapter();
adapter.setItems(Arrays.asList(
new PreferenceItem().setIcon(R.drawable.camera_outline_28).setTitle(ctx.getString(R.string.MenuFileAIGeneratorFromCamera)).setOnClickListener(v -> {
if (CloudController.getGeneratedModels() >= CloudController.getMaxGeneratedModels()) {
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.MenuFileAIGeneratorNoGenerationsLeft));
return;
}
if (MainActivity.IS_GENERATING_AI_MODEL) {
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.WARNING, R.string.MenuFileAIGeneratorAlreadyGenerating));
return;
}
if (ctx instanceof MainActivity) {
try {
MainActivity.aiTempFile = File.createTempFile("ai_capture", ".jpg");
Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
i.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(ctx, BuildConfig.APPLICATION_ID + ".provider", MainActivity.aiTempFile));
i.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
((MainActivity) ctx).startActivityForResult(i, MainActivity.REQUEST_CODE_AI_GENERATOR_TAKE_PHOTO);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}),
new PreferenceItem().setIcon(R.drawable.picture_outline_28).setTitle(ctx.getString(R.string.MenuFileAIGeneratorFromGallery)).setOnClickListener(v -> {
if (CloudController.getGeneratedModels() >= CloudController.getMaxGeneratedModels()) {
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.MenuFileAIGeneratorNoGenerationsLeft));
return;
}
if (MainActivity.IS_GENERATING_AI_MODEL) {
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.WARNING, R.string.MenuFileAIGeneratorAlreadyGenerating));
return;
}
if (ctx instanceof MainActivity) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
((MainActivity) ctx).startActivityForResult(Intent.createChooser(intent, ""), MainActivity.REQUEST_CODE_AI_GENERATOR_CHOOSE_PHOTO);
}
})
));
rv.setAdapter(adapter);
ll.addView(rv, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
ll.addView(new DividerView(ctx), new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(1f)));
remainingView = new TextView(ctx);
remainingView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
remainingView.setGravity(Gravity.CENTER);
remainingView.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
ll.addView(remainingView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(18)) {{
topMargin = ViewUtils.dp(8);
}});
segmentsView = new SegmentsView(ctx) {
@Override
protected int onGetColor(int i) {
return i == 1 ? ThemesRepo.getColor(android.R.attr.textColorSecondary) : ThemesRepo.getColor(android.R.attr.colorAccent);
}
};
ll.addView(segmentsView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(12)) {{
leftMargin = rightMargin = ViewUtils.dp(12);
topMargin = bottomMargin = ViewUtils.dp(8);
}});
updateRemaining();
ll.addView(new DividerView(ctx), new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(1f)));
LinearLayout toolbar = new LinearLayout(ctx);
toolbar.setPadding(ViewUtils.dp(12), 0, ViewUtils.dp(12), 0);
toolbar.setOrientation(LinearLayout.HORIZONTAL);
toolbar.setGravity(Gravity.CENTER_VERTICAL);
toolbar.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 0));
toolbar.setOnClickListener(v -> dismiss());
ImageView icon = new ImageView(ctx);
icon.setImageResource(R.drawable.arrow_left_outline_28);
icon.setColorFilter(ThemesRepo.getColor(android.R.attr.textColorSecondary));
toolbar.addView(icon, new LinearLayout.LayoutParams(ViewUtils.dp(28), ViewUtils.dp(28)));
TextView title = new TextView(ctx);
title.setText(R.string.MenuOrientationPositionBack);
title.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
title.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
toolbar.addView(title, new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f) {{
leftMargin = ViewUtils.dp(12);
}});
ll.addView(toolbar, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)));
return ll;
}
@Override
protected void onCreate() {
super.onCreate();
SliceBeam.EVENT_BUS.registerListener(this);
ViewUtils.postOnMainThread(() -> segmentsView.startAnimation(), 50);
}
@EventHandler(runOnMainThread = true)
public void onDismiss(NeedDismissAIGeneratorMenu e) {
dismiss();
}
@EventHandler(runOnMainThread = true)
public void onRemainingUpdated(CloudModelsRemainingCountUpdatedEvent e) {
updateRemaining();
}
@Override
protected void onDestroy() {
super.onDestroy();
SliceBeam.EVENT_BUS.unregisterListener(this);
}
private void updateRemaining() {
int rev = CloudController.getMaxGeneratedModels() - CloudController.getGeneratedModels();
remainingView.setText(SliceBeam.INSTANCE.getString(R.string.MenuFileAIGeneratorRemaining, rev, CloudController.getMaxGeneratedModels()));
segmentsView.setValues(new float[]{0, rev / (float) CloudController.getMaxGeneratedModels(), 1});
}
}
public final class CalibrationsMenu extends UnfoldMenu {
@Override
public int getRequestedSize(FrameLayout into, boolean portrait) {
return (int) (portrait ? into.getHeight() * 0.35f : into.getWidth() * 0.6f);
}
@@ -32,6 +32,8 @@ public abstract class ListBedMenu extends BedMenu {
recyclerView = new RecyclerView(ctx);
recyclerView.setLayoutManager(new LinearLayoutManager(ctx, portrait ? RecyclerView.HORIZONTAL : RecyclerView.VERTICAL, false));
recyclerView.setItemAnimator(null);
recyclerView.setClipToPadding(false);
recyclerView.setClipChildren(false);
adapter = new SimpleRecyclerAdapter() {
@NonNull
@Override
@@ -19,6 +19,8 @@ import android.widget.TextView;
import androidx.core.content.ContextCompat;
import com.google.android.material.snackbar.Snackbar;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
@@ -28,6 +30,8 @@ import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
import ru.ytkab0bp.slicebeam.events.FlattenModeResetEvent;
import ru.ytkab0bp.slicebeam.events.LongClickTranslationEvent;
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.ObjectsListChangedEvent;
import ru.ytkab0bp.slicebeam.events.SelectedObjectChangedEvent;
@@ -52,31 +56,82 @@ public class OrientationMenu extends ListBedMenu {
return Arrays.asList(
new BedMenuItem(R.string.MenuOrientationArrange, R.drawable.grid_layout_outline_28).onClick(v -> {
fragment.getGlView().arrange();
fragment.getGlView().queueEvent(() -> {
if (fragment.getGlView().getRenderer().invalidateFlattenMode()) {
fragment.getGlView().requestRender();
}
});
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuOrientationArrangeFinished));
}).setEnabled(fragment.getGlView().getRenderer().getModel() != null),
new SpaceItem(portrait ? ViewUtils.dp(8) : 0, portrait ? 0 : ViewUtils.dp(8)),
new BedMenuItem(R.string.MenuOrientationPosition, R.drawable.menu_orientation_position_28).setEnabled(hasSelection()).onClick(v -> fragment.showUnfoldMenu(new PositionMenu(), v)),
new BedMenuItem(R.string.MenuOrientationRotation, R.drawable.menu_orientation_rotation_28).setEnabled(hasSelection()).onClick(v -> fragment.showUnfoldMenu(new RotationMenu(), v))
new BedMenuItem(R.string.MenuOrientationAutoOrient, R.drawable.menu_orientation_auto_28).setEnabled(hasSelection()).onClick(view -> {
if (fragment.getGlView().getRenderer().resetFlattenMode()) {
fragment.getGlView().requestRender();
((BedMenuItem) adapter.getItems().get(3)).isChecked = false;
adapter.notifyItemChanged(3);
}
int i = fragment.getGlView().getRenderer().getSelectedObject();
fragment.getGlView().getRenderer().getModel().autoOrient(i);
fragment.getGlView().getRenderer().invalidateGlModel(i);
fragment.getGlView().requestRender();
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuOrientationAutoOrientDone, Snackbar.LENGTH_SHORT));
}),
new BedMenuItem(R.string.MenuOrientationFlatten, R.drawable.menu_orientation_flatten_28).setEnabled(hasSelection()).setCheckable((buttonView, isChecked) -> {
fragment.getGlView().getRenderer().setInFlattenMode(isChecked);
fragment.getGlView().requestRender();
}, false),
new BedMenuItem(R.string.MenuOrientationPosition, R.drawable.menu_orientation_position_28).setEnabled(hasSelection()).onClick(v -> {
if (fragment.getGlView().getRenderer().resetFlattenMode()) {
fragment.getGlView().requestRender();
((BedMenuItem) adapter.getItems().get(3)).isChecked = false;
adapter.notifyItemChanged(3);
}
fragment.showUnfoldMenu(new PositionMenu(), v);
}),
new BedMenuItem(R.string.MenuOrientationRotation, R.drawable.menu_orientation_rotation_28).setEnabled(hasSelection()).onClick(v -> {
if (fragment.getGlView().getRenderer().resetFlattenMode()) {
fragment.getGlView().requestRender();
((BedMenuItem) adapter.getItems().get(3)).isChecked = false;
adapter.notifyItemChanged(3);
}
fragment.showUnfoldMenu(new RotationMenu(), v);
})
);
}
@EventHandler(runOnMainThread = true)
public void onFlattenModeReset(FlattenModeResetEvent e) {
((BedMenuItem) adapter.getItems().get(3)).isChecked = false;
adapter.notifyItemChanged(3);
}
@EventHandler(runOnMainThread = true)
public void onObjectsChanged(ObjectsListChangedEvent e) {
((BedMenuItem) adapter.getItems().get(0)).setEnabled(fragment.getGlView().getRenderer().getModel() != null);
adapter.notifyItemChanged(0);
((BedMenuItem) adapter.getItems().get(2)).setEnabled(hasSelection());
adapter.notifyItemChanged(2);
((BedMenuItem) adapter.getItems().get(3)).setEnabled(hasSelection());
adapter.notifyItemChanged(3);
for (int i = 2; i <= 5; i++) {
BedMenuItem item = (BedMenuItem) adapter.getItems().get(i);
item.setEnabled(hasSelection());
if (item.isCheckable) {
item.isChecked = false;
}
adapter.notifyItemChanged(i);
}
}
@EventHandler(runOnMainThread = true)
public void onSelectionChanged(SelectedObjectChangedEvent e) {
((BedMenuItem) adapter.getItems().get(2)).setEnabled(hasSelection());
adapter.notifyItemChanged(2);
((BedMenuItem) adapter.getItems().get(3)).setEnabled(hasSelection());
adapter.notifyItemChanged(3);
for (int i = 2; i <= 5; i++) {
BedMenuItem item = (BedMenuItem) adapter.getItems().get(i);
item.setEnabled(hasSelection());
if (item.isCheckable) {
item.isChecked = false;
}
adapter.notifyItemChanged(i);
}
}
public final class PositionMenu extends UnfoldMenu {
@@ -85,7 +140,7 @@ public class OrientationMenu extends ListBedMenu {
private Vec3d tempVec = new Vec3d();
private int startedScrollObject;
private void translateVisual(Double x, Double y, Double z) {
public void translateVisual(Double x, Double y, Double z) {
int j = fragment.getGlView().getRenderer().getSelectedObject();
if (j == -1) return;
startedScrollObject = j;
@@ -341,6 +396,24 @@ public class OrientationMenu extends ListBedMenu {
startedScrollObject = -1;
}
@EventHandler(runOnMainThread = true)
public void onLongClickTranslation(LongClickTranslationEvent e) {
if (e.visual) {
int j = fragment.getGlView().getRenderer().getSelectedObject();
if (j == -1) return;
Model model = fragment.getGlView().getRenderer().getModel();
model.getTranslation(j, tempVec);
xTitle.setText(formatTrackTitle(R.string.MenuOrientationPositionXValue, tempVec.x + e.x));
yTitle.setText(formatTrackTitle(R.string.MenuOrientationPositionYValue, tempVec.y + e.y));
xTrack.setCurrentPosition((int) (tempVec.x + e.x));
yTrack.setCurrentPosition((int) (tempVec.y + e.y));
} else {
setSelectionValues();
}
}
@EventHandler(runOnMainThread = true)
public void onSelectedObjectChanged(SelectedObjectChangedEvent e) {
stopScroll();
@@ -418,8 +491,7 @@ public class OrientationMenu extends ListBedMenu {
dz %= 360;
model.rotate(j, Math.toRadians(dx), Math.toRadians(dy), Math.toRadians(dz));
model.getBoundingBoxExact(j, bbMin, bbMax);
model.translate(j, 0, 0, -bbMin.z);
model.ensureOnBed(j);
fragment.getGlView().getRenderer().setSelectionRotation(0, 0, 0);
fragment.getGlView().getRenderer().invalidateGlModel(j);
@@ -41,6 +41,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import cz.msebera.android.httpclient.Header;
import cz.msebera.android.httpclient.entity.ContentType;
@@ -52,6 +53,7 @@ import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
@@ -64,13 +66,14 @@ import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.DividerView;
import ru.ytkab0bp.slicebeam.view.PositionScrollView;
import ru.ytkab0bp.slicebeam.view.SegmentsView;
import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
public class SliceMenu extends ListBedMenu {
private AsyncHttpClient client = new AsyncHttpClient();
{
client.setLoggingEnabled(true);
client.setMaxRetriesAndTimeout(0, 5000);
client.setMaxRetriesAndTimeout(0, 10000);
}
private final static List<String> SUPPORTED_SEND = Collections.singletonList("octoprint");
@@ -132,7 +135,8 @@ public class SliceMenu extends ListBedMenu {
if (!host.startsWith("http://")) {
host = "http://" + host;
}
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuSliceSendToPrinterStarted));
String tag = UUID.randomUUID().toString();
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuSliceSendToPrinterLoading).tag(tag));
Header[] headers = TextUtils.isEmpty(apiKey) ? new Header[0] : new Header[] {new BasicHeader("X-Api-Key", apiKey)};
RequestParams params = new RequestParams();
try {
@@ -151,7 +155,8 @@ public class SliceMenu extends ListBedMenu {
if (!obj.has("action") && !obj.has("files")) {
throw new JSONException(obj.toString());
}
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(print ? R.string.MenuSliceSendToPrinterPrintStarted : R.string.MenuSliceSendToPrinterOK));
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(print ? SnackbarsLayout.Type.INFO : SnackbarsLayout.Type.DONE, print ? R.string.MenuSliceSendToPrinterPrintStarted : R.string.MenuSliceSendToPrinterOK));
} catch (JSONException e) {
onFailure(statusCode, headers, responseBody, e);
}
@@ -159,6 +164,7 @@ public class SliceMenu extends ListBedMenu {
@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(fragment.getContext())
.setTitle(R.string.MenuSliceSendToPrinterFailed)
.setMessage(error.toString())
@@ -22,6 +22,7 @@ import androidx.core.content.ContextCompat;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean;
import ru.ytkab0bp.eventbus.EventHandler;
import ru.ytkab0bp.slicebeam.R;
@@ -152,18 +153,21 @@ public class TransformMenu extends ListBedMenu {
if (j == -1) return;
startedScrollObject = j;
Model model = fragment.getGlView().getRenderer().getModel();
model.getScale(j, tempVec);
double scaleX = tempVec.x, scaleY = tempVec.y, scaleZ = tempVec.z;
model.getBoundingBoxExact(j, tempVec, tempVec2);
if (x != null) {
xTitle.setText(formatTrackTitle(R.string.MenuTransformScaleXValue, x * 100));
xTitle.setText(formatTrackTitle(R.string.MenuTransformScaleXValue, x * 100, (tempVec2.x - tempVec.x) / scaleX * x));
}
if (y != null) {
yTitle.setText(formatTrackTitle(R.string.MenuTransformScaleYValue, y * 100));
yTitle.setText(formatTrackTitle(R.string.MenuTransformScaleYValue, y * 100, (tempVec2.y - tempVec.y) / scaleY * y));
}
if (z != null) {
zTitle.setText(formatTrackTitle(R.string.MenuTransformScaleZValue, z * 100));
zTitle.setText(formatTrackTitle(R.string.MenuTransformScaleZValue, z * 100, (tempVec2.z - tempVec.z) / scaleZ * z));
}
Model model = fragment.getGlView().getRenderer().getModel();
model.getRotation(j, tempVec);
DoubleMatrix.setIdentityM(tempMatrix, 0);
DoubleMatrix.rotateM(tempMatrix, 0, Math.toDegrees(tempVec.x), 1, 0, 0);
@@ -197,19 +201,22 @@ public class TransformMenu extends ListBedMenu {
fragment.getGlView().queueEvent(() -> {
Model model = fragment.getGlView().getRenderer().getModel();
model.getScale(j, tempVec);
double scaleX = tempVec.x, scaleY = tempVec.y, scaleZ = tempVec.z;
model.getBoundingBoxExact(j, tempVec, tempVec2);
double dx = 1f, dy = 1f, dz = 1f;
if (x != null) {
dx = x;
xTitle.setText(formatTrackTitle(R.string.MenuTransformScaleXValue, x * 100));
xTitle.setText(formatTrackTitle(R.string.MenuTransformScaleXValue, x * 100, (tempVec2.x - tempVec.x) / scaleX * x));
}
if (y != null) {
dy = y;
yTitle.setText(formatTrackTitle(R.string.MenuTransformScaleYValue, y * 100));
yTitle.setText(formatTrackTitle(R.string.MenuTransformScaleYValue, y * 100, (tempVec2.y - tempVec.y) / scaleY * y));
}
if (z != null) {
dz = z;
zTitle.setText(formatTrackTitle(R.string.MenuTransformScaleZValue, z * 100));
zTitle.setText(formatTrackTitle(R.string.MenuTransformScaleZValue, z * 100, (tempVec2.z - tempVec.z) / scaleZ * z));
}
model.getRotation(j, tempVec);
@@ -233,9 +240,7 @@ public class TransformMenu extends ListBedMenu {
model.getScale(j, tempVec);
model.scale(j, dx, dy, dz);
model.getBoundingBoxExact(j, tempVec, tempVec2);
model.translate(j, 0, 0, -tempVec.z);
model.ensureOnBed(j);
fragment.getGlView().getRenderer().invalidateSelectionObject();
fragment.getGlView().getRenderer().setSelectionScale(1, 1, 1);
@@ -245,8 +250,8 @@ public class TransformMenu extends ListBedMenu {
fragment.getGlView().requestRender();
}
private CharSequence formatTrackTitle(int res, double value) {
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(SliceBeam.INSTANCE.getString(res, value));
private CharSequence formatTrackTitle(int res, double value, double mm) {
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(SliceBeam.INSTANCE.getString(res, value, mm));
sb.append(" d");
int size = ViewUtils.dp(14);
Drawable dr = ContextCompat.getDrawable(SliceBeam.INSTANCE, R.drawable.edit_outline_28);
@@ -263,33 +268,108 @@ public class TransformMenu extends ListBedMenu {
Model model = fragment.getGlView().getRenderer().getModel();
model.getScale(j, tempVec);
double current;
Context ctx = getView().getContext();
LinearLayout ll = new LinearLayout(ctx);
ll.setOrientation(LinearLayout.VERTICAL);
AtomicBoolean inputInMM = new AtomicBoolean(Prefs.isScaleInputInMM());
double cur;
if (x) {
current = tempVec.x * 100;
cur = tempVec.x * 100;
} else if (y) {
current = tempVec.y * 100;
cur = tempVec.y * 100;
} else {
current = tempVec.z * 100;
cur = tempVec.z * 100;
}
Context ctx = getView().getContext();
FrameLayout fl = new FrameLayout(ctx);
model.getScale(j, tempVec);
double scaleX = tempVec.x, scaleY = tempVec.y, scaleZ = tempVec.z;
model.getBoundingBoxExact(j, tempVec, tempVec2);
if (inputInMM.get()) {
cur /= 100.0;
if (x) {
cur *= (tempVec2.x - tempVec.x) / scaleX;
} else if (y) {
cur *= (tempVec2.y - tempVec.y) / scaleY;
} else {
cur *= (tempVec2.z - tempVec.z) / scaleZ;
}
}
double current = cur;
EditText text = new EditText(ctx);
text.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
text.setText(String.format(Locale.ROOT, "%.2f", current));
text.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
fl.addView(text, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
ll.addView(text, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
leftMargin = rightMargin = ViewUtils.dp(21);
}});
PreferenceSwitchItem.SwitchPreferenceHolderView holderView = new PreferenceSwitchItem.SwitchPreferenceHolderView(ctx);
holderView.title.setText(R.string.MenuTransformScaleMM);
holderView.title.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
holderView.matSwitch.setChecked(inputInMM.get());
holderView.subtitle.setVisibility(View.GONE);
holderView.setOnClickListener(v -> {
inputInMM.set(!inputInMM.get());
double value;
try {
value = Double.parseDouble(text.getText().toString());
} catch (NumberFormatException e) {
value = current;
}
if (inputInMM.get()) {
value /= 100.0;
if (x) {
value *= (tempVec2.x - tempVec.x) / scaleX;
} else if (y) {
value *= (tempVec2.y - tempVec.y) / scaleY;
} else {
value *= (tempVec2.z - tempVec.z) / scaleZ;
}
} else {
if (x) {
value /= (tempVec2.x - tempVec.x) / scaleX;
} else if (y) {
value /= (tempVec2.y - tempVec.y) / scaleY;
} else {
value /= (tempVec2.z - tempVec.z) / scaleZ;
}
value *= 100.0;
}
text.setText(String.format(Locale.ROOT, "%.2f", value));
holderView.matSwitch.setChecked(inputInMM.get());
Prefs.setScaleInputInMM(inputInMM.get());
});
ll.addView(holderView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
leftMargin = rightMargin = ViewUtils.dp(8);
}});
new BeamAlertDialogBuilder(ctx)
.setTitle(title)
.setView(fl)
.setView(ll)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
double value;
try {
value = Double.parseDouble(text.getText().toString()) / 100.0;
value = Double.parseDouble(text.getText().toString());
if (inputInMM.get()) {
if (x) {
value /= (tempVec2.x - tempVec.x) / scaleX;
} else if (y) {
value /= (tempVec2.y - tempVec.y) / scaleY;
} else {
value /= (tempVec2.z - tempVec.z) / scaleZ;
}
} else {
value /= 100.0;
}
} catch (NumberFormatException e) {
value = current;
}
@@ -449,14 +529,16 @@ public class TransformMenu extends ListBedMenu {
Model model = fragment.getGlView().getRenderer().getModel();
model.getScale(j, tempVec);
double scaleX = tempVec.x, scaleY = tempVec.y, scaleZ = tempVec.z;
xTrack.setCurrentPosition((int) Math.round(tempVec.x * 100));
yTrack.setCurrentPosition((int) Math.round(tempVec.y * 100));
zTrack.setCurrentPosition((int) Math.round(tempVec.z * 100));
xTrack.setCurrentPosition((int) Math.round(scaleX * 100));
yTrack.setCurrentPosition((int) Math.round(scaleY * 100));
zTrack.setCurrentPosition((int) Math.round(scaleZ * 100));
xTitle.setText(formatTrackTitle(R.string.MenuTransformScaleXValue, tempVec.x * 100));
yTitle.setText(formatTrackTitle(R.string.MenuTransformScaleYValue, tempVec.y * 100));
zTitle.setText(formatTrackTitle(R.string.MenuTransformScaleZValue, tempVec.z * 100));
model.getBoundingBoxExact(j, tempVec, tempVec2);
xTitle.setText(formatTrackTitle(R.string.MenuTransformScaleXValue, scaleX * 100, (tempVec2.x - tempVec.x)));
yTitle.setText(formatTrackTitle(R.string.MenuTransformScaleYValue, scaleY * 100, (tempVec2.y - tempVec.y)));
zTitle.setText(formatTrackTitle(R.string.MenuTransformScaleZValue, scaleZ * 100, (tempVec2.z - tempVec.z)));
xTrack.updateSyncDeltas();
yTrack.updateSyncDeltas();
@@ -34,10 +34,25 @@ public class ConfigObject implements ProfileListFragment.ProfileListItem {
this.values.putAll(from.values);
}
/**
* Note: suitable only from "printer" config
*/
public int getExtruderCount() {
return get("nozzle_diameter") != null ? get("nozzle_diameter").replaceAll("[^.]+", "").length() : 1;
}
public boolean has(String key) {
return values.containsKey(key);
}
public String get(String key) {
return values.get(key);
}
public void remove(String key) {
values.remove(key);
}
public void put(String key, String value) {
values.put(key, value);
}
@@ -0,0 +1,6 @@
package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event;
@Event
public class CloudFeaturesUpdatedEvent {}
@@ -0,0 +1,6 @@
package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event;
@Event
public class CloudLoginStateUpdatedEvent {}
@@ -0,0 +1,6 @@
package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event;
@Event
public class CloudModelsRemainingCountUpdatedEvent {}
@@ -0,0 +1,6 @@
package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event;
@Event
public class CloudSyncFinishedEvent {}
@@ -0,0 +1,7 @@
package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event;
@Event
public class CloudUserInfoUpdatedEvent {
}
@@ -0,0 +1,7 @@
package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event;
@Event
public class FlattenModeResetEvent {
}
@@ -0,0 +1,16 @@
package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event;
@Event
public class LongClickTranslationEvent {
public final double x;
public final double y;
public final boolean visual;
public LongClickTranslationEvent(double x, double y, boolean visual) {
this.x = x;
this.y = y;
this.visual = visual;
}
}
@@ -0,0 +1,6 @@
package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event;
@Event
public class NeedDismissAIGeneratorMenu {}
@@ -0,0 +1,12 @@
package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event;
@Event
public class NeedDismissSnackbarEvent {
public final String tag;
public NeedDismissSnackbarEvent(String tag) {
this.tag = tag;
}
}
@@ -1,11 +1,24 @@
package ru.ytkab0bp.slicebeam.events;
import android.view.View;
import ru.ytkab0bp.eventbus.Event;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
@Event
public class NeedSnackbarEvent {
public final CharSequence title;
public SnackbarsLayout.Type type = SnackbarsLayout.Type.DONE;
public String tag;
public CharSequence buttonTitle;
public View.OnClickListener buttonClick;
public NeedSnackbarEvent(SnackbarsLayout.Type type, CharSequence title) {
this.type = type;
this.title = title;
}
public NeedSnackbarEvent(CharSequence title) {
this.title = title;
@@ -14,4 +27,20 @@ public class NeedSnackbarEvent {
public NeedSnackbarEvent(int title, Object... args) {
this.title = SliceBeam.INSTANCE.getString(title, args);
}
public NeedSnackbarEvent(SnackbarsLayout.Type type, int title, Object... args) {
this.type = type;
this.title = SliceBeam.INSTANCE.getString(title, args);
}
public NeedSnackbarEvent tag(String tag) {
this.tag = tag;
return this;
}
public NeedSnackbarEvent button(int title, View.OnClickListener click) {
this.buttonTitle = SliceBeam.INSTANCE.getString(title);
this.buttonClick = click;
return this;
}
}
@@ -1,23 +1,33 @@
package ru.ytkab0bp.slicebeam.fragment;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.res.ColorStateList;
import android.os.Process;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.dynamicanimation.animation.FloatValueHolder;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import com.google.android.material.navigation.NavigationBarView;
import com.google.android.material.snackbar.Snackbar;
import java.io.File;
@@ -33,6 +43,9 @@ import ru.ytkab0bp.slicebeam.components.bed_menu.FileMenu;
import ru.ytkab0bp.slicebeam.components.bed_menu.OrientationMenu;
import ru.ytkab0bp.slicebeam.components.bed_menu.SliceMenu;
import ru.ytkab0bp.slicebeam.components.bed_menu.TransformMenu;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.events.FlattenModeResetEvent;
import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.SlicingProgressEvent;
import ru.ytkab0bp.slicebeam.navigation.Fragment;
@@ -43,8 +56,10 @@ import ru.ytkab0bp.slicebeam.slic3r.Slic3rRuntimeError;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.Vec3d;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.BedSwipeDownLayout;
import ru.ytkab0bp.slicebeam.view.DividerView;
import ru.ytkab0bp.slicebeam.view.GLView;
import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
import ru.ytkab0bp.slicebeam.view.ThemeBottomNavigationView;
import ru.ytkab0bp.slicebeam.view.ThemeRailNavigationView;
@@ -53,7 +68,7 @@ public class BedFragment extends Fragment {
private final static int MENU_SIZE_DP = 80;
private FrameLayout overlayLayout;
private CoordinatorLayout snackbarsLayout;
private SnackbarsLayout snackbarsLayout;
private GLView glView;
private NavigationBarView navigationView;
@@ -97,6 +112,14 @@ public class BedFragment extends Fragment {
private GCodeProcessorResult gCodeResult;
private UnfoldMenu currentUnfoldMenu;
private BedSwipeDownLayout swipeDownLayout;
private boolean hasWebError;
private WebView panelWebView;
private LinearLayout panelWebViewError;
private ImageView webViewErrIcon;
private TextView webViewErrDescription;
private ProgressBar webViewProgressBar;
private static String tempFileName;
private static File tempExportingFile;
@@ -117,7 +140,21 @@ public class BedFragment extends Fragment {
@EventHandler(runOnMainThread = true)
public void onNeedSnackbar(NeedSnackbarEvent e) {
Snackbar.make(snackbarsLayout, e.title, Snackbar.LENGTH_SHORT).show();
SnackbarsLayout.Snackbar s = new SnackbarsLayout.Snackbar(e.type, e.title);
if (e.tag != null) {
s.tag(e.tag);
}
if (e.buttonTitle != null) {
s.lifetime = 0;
s.buttonTitle = e.buttonTitle;
s.buttonClick = e.buttonClick;
}
snackbarsLayout.show(s);
}
@EventHandler(runOnMainThread = true)
public void onDismissSnackbar(NeedDismissSnackbarEvent e) {
snackbarsLayout.dismiss(e.tag);
}
public void showUnfoldMenu(UnfoldMenu menu, View from) {
@@ -158,6 +195,9 @@ public class BedFragment extends Fragment {
currentUnfoldMenu.dismiss();
return true;
}
if (swipeDownLayout.onBackPressed()) {
return true;
}
if (currentMenuSlot != 0) {
navigationView.setSelectedItemId(0);
return true;
@@ -191,6 +231,33 @@ public class BedFragment extends Fragment {
public void onResume() {
super.onResume();
glView.onResume();
ConfigObject cfg = SliceBeam.CONFIG.findPrinter(SliceBeam.CONFIG.presets.get("printer"));
boolean enable = cfg != null && cfg.get("host_type") != null && !TextUtils.isEmpty(cfg.get("print_host")) && panelWebView != null;
swipeDownLayout.setEnableTop(enable);
if (enable) {
String host = cfg.get("print_host");
if (host.contains(":")) {
try {
int port = Integer.parseInt(host.split(":")[1]);
if (port >= 7125 && port <= 7200) {
host = host.split(":")[0];
}
} catch (Exception ignored) {}
}
if (!host.startsWith("http://")) {
host = "http://" + host;
}
webViewProgressBar.animate().alpha(1).setDuration(150).start();
panelWebView.setAlpha(0f);
hasWebError = false;
panelWebView.loadUrl(host);
panelWebViewError.animate().alpha(0).setDuration(150).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
panelWebViewError.setVisibility(View.GONE);
}
}).start();
}
}
@Override
@@ -199,6 +266,7 @@ public class BedFragment extends Fragment {
glView.onPause();
}
@SuppressLint("SetJavaScriptEnabled")
@Override
public View onCreateView(Context ctx) {
glView = new GLView(ctx);
@@ -229,7 +297,73 @@ public class BedFragment extends Fragment {
ll.addView(menuView, new LinearLayout.LayoutParams(ViewUtils.dp(MENU_SIZE_DP), ViewGroup.LayoutParams.MATCH_PARENT));
}
ll.addView(glView, new LinearLayout.LayoutParams(portrait ? ViewGroup.LayoutParams.MATCH_PARENT : 0, portrait ? 0 : ViewGroup.LayoutParams.MATCH_PARENT, 1f));
swipeDownLayout = new BedSwipeDownLayout(ctx);
FrameLayout wfl = new FrameLayout(ctx);
try {
panelWebView = new WebView(ctx);
panelWebView.getSettings().setJavaScriptEnabled(true);
panelWebView.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
hasWebError = true;
webViewErrDescription.setText(description);
panelWebViewError.setVisibility(View.VISIBLE);
panelWebViewError.setAlpha(0f);
panelWebViewError.animate().alpha(1).setDuration(150).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
panelWebView.setVisibility(View.GONE);
}
}).start();
webViewProgressBar.animate().alpha(0).setDuration(150).start();
}
@Override
public void onPageFinished(WebView view, String url) {
if (!hasWebError) {
panelWebView.animate().alpha(1).setDuration(150).start();
webViewProgressBar.animate().alpha(0).setDuration(150).start();
}
}
});
wfl.addView(panelWebView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
panelWebViewError = new LinearLayout(ctx);
panelWebViewError.setVisibility(View.GONE);
panelWebViewError.setOrientation(LinearLayout.VERTICAL);
panelWebViewError.setGravity(Gravity.CENTER);
panelWebViewError.setPadding(ViewUtils.dp(12), ViewUtils.dp(12), ViewUtils.dp(12), ViewUtils.dp(12));
webViewErrIcon = new ImageView(ctx);
webViewErrIcon.setImageResource(R.drawable.globe_cross_outline_28);
panelWebViewError.addView(webViewErrIcon, new LinearLayout.LayoutParams(ViewUtils.dp(28), ViewUtils.dp(28)) {{
bottomMargin = ViewUtils.dp(8);
}});
webViewErrDescription = new TextView(ctx);
webViewErrDescription.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
webViewErrDescription.setGravity(Gravity.CENTER);
panelWebViewError.addView(webViewErrDescription);
wfl.addView(panelWebViewError, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
webViewProgressBar = new ProgressBar(ctx);
webViewProgressBar.setAlpha(0f);
wfl.addView(webViewProgressBar, new FrameLayout.LayoutParams(ViewUtils.dp(36), ViewUtils.dp(36), Gravity.CENTER));
} catch (Exception e) {
Log.wtf("BedFragment", "Failed to initialize webview", e);
}
if (portrait) {
LinearLayout inner = new LinearLayout(ctx);
inner.setOrientation(LinearLayout.VERTICAL);
ll = inner;
inner.addView(glView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
swipeDownLayout.addView(inner);
swipeDownLayout.addView(wfl);
} else {
swipeDownLayout.addView(glView);
swipeDownLayout.addView(wfl);
ll.addView(swipeDownLayout, new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1f));
}
if (portrait) {
ll.addView(menuView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(MENU_SIZE_DP)));
@@ -328,8 +462,12 @@ public class BedFragment extends Fragment {
return true;
});
overlayLayout.addView(contentView = ll);
overlayLayout.addView(snackbarsLayout = new CoordinatorLayout(ctx), new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) {{
if (portrait) {
overlayLayout.addView(contentView = swipeDownLayout);
} else {
overlayLayout.addView(contentView = ll);
}
overlayLayout.addView(snackbarsLayout = new SnackbarsLayout(ctx), new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) {{
if (portrait) {
bottomMargin = ViewUtils.dp(80 * 2);
} else {
@@ -339,7 +477,7 @@ public class BedFragment extends Fragment {
return overlayLayout;
}
public CoordinatorLayout getSnackbarsLayout() {
public SnackbarsLayout getSnackbarsLayout() {
return snackbarsLayout;
}
@@ -348,6 +486,10 @@ public class BedFragment extends Fragment {
}
private void selectMenu(Context ctx, boolean portrait, int slot) {
if (glView.getRenderer().resetFlattenMode()) {
glView.requestRender();
SliceBeam.EVENT_BUS.fireEvent(new FlattenModeResetEvent());
}
isAnimatingMenu = true;
BedMenu prevMenu = menuMap.get(currentMenuSlot);
@@ -443,7 +585,6 @@ public class BedFragment extends Fragment {
}
});
} else {
glView.getRenderer().setModel(model = m);
glView.queueEvent(new Runnable() {
@Override
public void run() {
@@ -452,6 +593,8 @@ public class BedFragment extends Fragment {
ViewUtils.postOnMainThread(()-> glView.queueEvent(this));
return;
}
glView.getRenderer().setModel(model = m);
Vec3d center = bed.getVolumeMin().center(bed.getVolumeMax());
Vec3d objMin = new Vec3d(), objMax = new Vec3d();
Vec3d objTranslate = new Vec3d();
@@ -471,6 +614,11 @@ public class BedFragment extends Fragment {
public void onApplyTheme() {
super.onApplyTheme();
if (panelWebView != null) {
webViewErrIcon.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.textColorSecondary)));
webViewErrDescription.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
webViewProgressBar.setIndeterminateTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.textColorSecondary)));
}
menuView.setBackgroundColor(ThemesRepo.getColor(android.R.attr.windowBackground));
for (int i = 0; i < MenuCategory.values().length; i++) {
if (i != currentMenuSlot) {
@@ -113,7 +113,7 @@ public class PrinterConfigFragment extends ProfileListFragment {
// TODO: m_supports_min_feedrates? <= marlin/marlin legacy
));
int count = currentConfig.get("nozzle_diameter") != null ? currentConfig.get("nozzle_diameter").replaceAll("[^.]+", "").length() : 1;
int count = currentConfig.getExtruderCount();
for (int i = 0; i < count; i++) {
int j = count == 1 ? -1 : i;
list.addAll(Arrays.asList(
@@ -6,6 +6,8 @@ import android.content.res.ColorStateList;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
@@ -34,6 +36,8 @@ import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import com.mrudultora.colorpicker.ColorPickerPopUp;
import java.util.ArrayList;
@@ -47,6 +51,8 @@ import java.util.concurrent.atomic.AtomicReference;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.cloud.CloudAPI;
import ru.ytkab0bp.slicebeam.cloud.CloudController;
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.components.BeamColorPickerPopUp;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
@@ -56,6 +62,7 @@ import ru.ytkab0bp.slicebeam.recycler.PreferenceItem;
import ru.ytkab0bp.slicebeam.recycler.PreferenceSwitchItem;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import ru.ytkab0bp.slicebeam.slic3r.ConfigOptionDef;
import ru.ytkab0bp.slicebeam.slic3r.PrintConfigDef;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rLocalization;
import ru.ytkab0bp.slicebeam.theme.IThemeView;
@@ -68,6 +75,8 @@ import ru.ytkab0bp.slicebeam.view.FadeRecyclerView;
import ru.ytkab0bp.slicebeam.view.ProfileDropdownView;
public abstract class ProfileListFragment extends Fragment {
public final static int SPECIAL_TYPE_CLOUD_HEADER = 0;
private final static Object ROTATION_PAYLOAD = new Object();
protected ProfileDropdownView dropdownView;
@@ -75,7 +84,7 @@ public abstract class ProfileListFragment extends Fragment {
protected ImageView resetButton;
protected BeamButton saveButton;
protected boolean changedConfig;
protected ConfigObject diffObject = new ConfigObject();
private List<OptionWrapper> currentList = Collections.emptyList();
private SparseArray<List<OptionWrapper>> categoryElements = new SparseArray<>();
@@ -146,8 +155,8 @@ public abstract class ProfileListFragment extends Fragment {
int pos = getChildViewHolder(ch).getAdapterPosition();
if (pos == -1 || ch.getAlpha() < 1) continue;
boolean top = currentList.get(pos).title != null;
boolean bottom = pos == getAdapter().getItemCount() - 1 || currentList.get(pos + 1).title != null;
boolean top = currentList.get(pos).title != null || currentList.get(pos).hasSpecialType();
boolean bottom = pos == getAdapter().getItemCount() - 1 || currentList.get(pos + 1).title != null || currentList.get(pos + 1).hasSpecialType();
if (top && startI != -1) {
c.drawRoundRect(0, getChildAt(startI).getTop() + getChildAt(startI).getTranslationY(), getWidth(), ch.getTop() + ch.getTranslationY() - ViewUtils.dp(8), ViewUtils.dp(32), ViewUtils.dp(32), bgPaint);
@@ -204,7 +213,7 @@ public abstract class ProfileListFragment extends Fragment {
};
recyclerView.setItemAnimator(new CubicBezierItemAnimator());
recyclerView.setAdapter(new RecyclerView.Adapter() {
private final static int TYPE_TITLE = 0, TYPE_SIMPLE = 1;
private final static int TYPE_TITLE = 0, TYPE_CLOUD_PROFILE = 1, TYPE_SIMPLE = 2;
private Map<Class<?>, Integer> viewType = new HashMap<>();
private Map<Integer, SimpleRecyclerItem> viewCreator = new HashMap<>();
@@ -219,6 +228,9 @@ public abstract class ProfileListFragment extends Fragment {
v = viewCreator.get(viewType).onCreateView(ctx);
break;
}
case TYPE_CLOUD_PROFILE:
v = new CloudProfileHeaderView(ctx);
break;
case TYPE_TITLE:
v = new CategoryHolderView(ctx);
break;
@@ -258,6 +270,43 @@ public abstract class ProfileListFragment extends Fragment {
el.simpleItem.onBindView(holder.itemView);
break;
}
case TYPE_CLOUD_PROFILE: {
OptionWrapper w = currentList.get(position);
CloudProfileHeaderView holderView = (CloudProfileHeaderView) holder.itemView;
holderView.setTag(w.color);
if (Prefs.getCloudAPIToken() != null) {
CloudAPI.UserInfo info = CloudController.getUserInfo();
if (info != null) {
if (!TextUtils.isEmpty(info.avatarUrl)) {
holderView.hasAvatar = true;
Glide.with(holderView.avatar)
.load(info.avatarUrl)
.circleCrop()
.transition(DrawableTransitionOptions.withCrossFade())
.into(holderView.avatar);
} else {
holderView.hasAvatar = false;
holderView.avatar.setImageResource(R.drawable.user_circle_outline_28);
}
holderView.title.setText(info.displayName);
} else {
holderView.hasAvatar = false;
holderView.avatar.setImageResource(R.drawable.user_circle_outline_28);
holderView.title.setText(R.string.SettingsCloudLoading);
}
holderView.subtitle.setText(R.string.SettingsCloudTapToManage);
} else {
holderView.hasAvatar = false;
holderView.avatar.setImageResource(R.drawable.user_circle_outline_28);
holderView.title.setText(R.string.SettingsCloudNotLoggedIn);
holderView.subtitle.setText(R.string.SettingsCloudTapToShowMore);
}
holderView.onApplyTheme();
holderView.setOnClickListener(view -> w.onClick.run());
break;
}
case TYPE_TITLE: {
OptionWrapper w = currentList.get(position);
CategoryHolderView holderView = (CategoryHolderView) holder.itemView;
@@ -301,6 +350,13 @@ public abstract class ProfileListFragment extends Fragment {
@Override
public int getItemViewType(int position) {
OptionWrapper w = currentList.get(position);
if (w.optionEl != null && w.optionEl.specialType != -1) {
switch (w.optionEl.specialType) {
default:
case SPECIAL_TYPE_CLOUD_HEADER:
return TYPE_CLOUD_PROFILE;
}
}
if (w.title != null) return TYPE_TITLE;
if (w.optionEl.simpleItem != null) {
@@ -332,21 +388,45 @@ public abstract class ProfileListFragment extends Fragment {
saveButton.setPadding(ViewUtils.dp(21), ViewUtils.dp(12), ViewUtils.dp(21), ViewUtils.dp(12));
saveButton.setOnClickListener(v -> {
FrameLayout fl = new FrameLayout(ctx);
LinearLayout linear = new LinearLayout(ctx);
linear.setOrientation(LinearLayout.VERTICAL);
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> en : diffObject.values.entrySet()) {
if (sb.length() > 0) {
sb.append("\n");
}
ConfigOptionDef def = PrintConfigDef.getInstance().options.get(en.getKey());
sb.append(Slic3rLocalization.getString(def.getFullLabel())).append(" - ").append(opt(def, -1));
}
if (sb.length() > 0) {
ScrollView scrollView = new ScrollView(ctx);
TextView subtitle = new TextView(ctx);
subtitle.setTextAppearance(ctx, com.google.android.material.R.style.MaterialAlertDialog_Material3_Body_Text);
subtitle.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
subtitle.setText(sb.toString());
subtitle.setPadding(ViewUtils.dp(24), ViewUtils.dp(12), ViewUtils.dp(24), ViewUtils.dp(12));
scrollView.addView(subtitle, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
linear.addView(scrollView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
}
EditText text = new EditText(ctx);
text.setText(getCurrentConfig().getTitle());
text.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
fl.addView(text, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
linear.addView(text, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
leftMargin = rightMargin = ViewUtils.dp(21);
}});
AlertDialog dialog = new BeamAlertDialogBuilder(ctx)
.setTitle(R.string.SettingsSaveTitle)
// TODO: Draw settings delta
.setView(fl)
.setView(linear)
.setPositiveButton(android.R.string.ok, (d, which) -> {
getCurrentConfig().values.putAll(diffObject.values);
diffObject.values.clear();
onApplyConfig(text.getText().toString());
resetButton.animate().alpha(0.6f).start();
resetButton.animate().alpha(0.4f).setDuration(150).start();
resetButton.setClickable(false);
onUpdateConfigItems();
})
@@ -395,13 +475,14 @@ public abstract class ProfileListFragment extends Fragment {
resetButton.setImageResource(R.drawable.refresh_outline_28);
resetButton.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.textColorPrimary)));
resetButton.setScaleX(-1f);
resetButton.setAlpha(0.6f);
resetButton.setAlpha(0.4f);
resetButton.setOnClickListener(v -> new BeamAlertDialogBuilder(ctx)
.setTitle(R.string.SettingsResetProfileTitle)
.setMessage(R.string.SettingsResetProfileDescription)
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
onResetConfig();
resetButton.animate().alpha(0.6f).start();
diffObject.values.clear();
resetButton.animate().alpha(0.4f).setDuration(150).start();
resetButton.setClickable(false);
onUpdateConfigItems();
})
@@ -433,6 +514,7 @@ public abstract class ProfileListFragment extends Fragment {
public void onDestroy() {
super.onDestroy();
SliceBeam.EVENT_BUS.unregisterListener(this);
unfolded.clear();
}
@SuppressLint("NotifyDataSetChanged")
@@ -443,7 +525,12 @@ public abstract class ProfileListFragment extends Fragment {
OptionElement el = items.get(i);
if (el == null) continue;
OptionWrapper w = el.title != null ? new OptionWrapper(el.icon, el.title, el.onClick, el.color, el.noTint) : new OptionWrapper(el);
if (el.title != null) {
if (el.specialType != -1) {
w.color = el.color;
w.noTint = el.noTint;
w.onClick = el.onClick;
}
if (el.title != null || el.specialType != -1) {
w.categoryIndex = j;
categoryElements.put(j, new ArrayList<>());
j++;
@@ -474,10 +561,16 @@ public abstract class ProfileListFragment extends Fragment {
}
private String opt(ConfigOptionDef def, int i) {
String v = getCurrentConfig().get(def.key);
return opt(def, i, false);
}
private String opt(ConfigOptionDef def, int i, boolean ignoreDiff) {
String v = ignoreDiff || !diffObject.has(def.key) ? getCurrentConfig().get(def.key) : diffObject.get(def.key);
if (i != -1) {
try {
v = v.split(",")[i];
String ch = ",";
if (def.guiType == ConfigOptionDef.GUIType.COLOR) ch = ";";
v = v.split(ch)[i];
} catch (ArrayIndexOutOfBoundsException e) {
Log.w("ProfileListFragment", "Failed to parse mm option", e);
}
@@ -487,17 +580,27 @@ public abstract class ProfileListFragment extends Fragment {
protected void updateConfigField(ConfigOptionDef def, int i, String value) {
if (i != -1) {
String[] vals = opt(def, -1).split(",");
String ch = ",";
if (def.guiType == ConfigOptionDef.GUIType.COLOR) ch = ";";
String[] vals = opt(def, -1, true).split(ch);
vals[i] = value;
value = TextUtils.join(",", vals);
value = TextUtils.join(ch, vals);
}
if (!Objects.equals(opt(def, i), value)) {
changedConfig = true;
resetButton.animate().alpha(1).start();
resetButton.setClickable(true);
boolean wasEmpty = diffObject.values.isEmpty();
if (Objects.equals(opt(def, i, true), value)) {
diffObject.remove(def.key);
} else {
diffObject.put(def.key, value);
}
boolean empty = diffObject.values.isEmpty();
if (wasEmpty && !empty) {
resetButton.animate().alpha(1.0f).setDuration(150).start();
resetButton.setClickable(true);
} else if (!wasEmpty && empty) {
resetButton.animate().alpha(0.4f).setDuration(150).start();
resetButton.setClickable(false);
}
getCurrentConfig().put(def.key, value);
}
@SuppressLint("NotifyDataSetChanged")
@@ -521,6 +624,8 @@ public abstract class ProfileListFragment extends Fragment {
}
public final class OptionElement {
public int specialType = -1;
public int icon;
public String title;
public int color;
@@ -580,7 +685,12 @@ public abstract class ProfileListFragment extends Fragment {
labels[i] = Slic3rLocalization.getString(def.enumLabels[i]);
}
}
builder.setSingleChoiceItems(labels, Arrays.asList(values).indexOf(opt(def, eIndex)), (dialog, which) -> {
String val = opt(def, eIndex);
int i = Arrays.asList(values).indexOf(val);
if (i == -1 && val.matches("^\\d+$")) {
i = Integer.parseInt(val);
}
builder.setSingleChoiceItems(labels, i, (dialog, which) -> {
updateConfigField(def, eIndex, values[which]);
// TODO: Update only value
recyclerView.getAdapter().notifyItemChanged(boundIndex);
@@ -692,6 +802,13 @@ public abstract class ProfileListFragment extends Fragment {
}
ref.set(builder.show());
}).setOnLongClickListener(v -> {
new BeamAlertDialogBuilder(getContext())
.setTitle(Slic3rLocalization.getString(def.getFullLabel()))
.setMessage(Slic3rLocalization.getString(def.tooltip))
.setPositiveButton(android.R.string.ok, null)
.show();
return true;
});
if (def.type == ConfigOptionDef.ConfigOptionType.STRING || def.type == ConfigOptionDef.ConfigOptionType.STRINGS) {
@@ -700,7 +817,21 @@ public abstract class ProfileListFragment extends Fragment {
((PreferenceItem) simpleItem).setTitle(null);
}
} else {
((PreferenceItem) simpleItem).setValueProvider(() -> def.type == ConfigOptionDef.ConfigOptionType.ENUM ? Slic3rLocalization.getString(def.enumLabels[Arrays.asList(def.enumValues).indexOf(opt(def, eIndex))]) : opt(def, eIndex));
((PreferenceItem) simpleItem).setValueProvider(() -> {
if (def.type == ConfigOptionDef.ConfigOptionType.ENUM) {
String v = opt(def, eIndex);
int i = Arrays.asList(def.enumValues).indexOf(v);
if (i != -1) {
return Slic3rLocalization.getString(def.enumLabels[i]);
} else if (v.matches("^\\d+$")) {
return Slic3rLocalization.getString(def.enumLabels[Integer.parseInt(v)]);
} else {
return v;
}
} else {
return opt(def, eIndex);
}
});
}
}
switch (def.type) {
@@ -708,11 +839,23 @@ public abstract class ProfileListFragment extends Fragment {
case BOOLS:
simpleItem = new PreferenceSwitchItem().setTitle(Slic3rLocalization.getString(def.label))
.setValueProvider(() -> "1".equals(opt(def, eIndex)))
.setChangeListener((buttonView, isChecked) -> updateConfigField(def, eIndex, String.valueOf(isChecked ? 1 : 0)));
.setChangeListener((buttonView, isChecked) -> updateConfigField(def, eIndex, String.valueOf(isChecked ? 1 : 0)))
.setLongClickListener(v -> {
new BeamAlertDialogBuilder(getContext())
.setTitle(Slic3rLocalization.getString(def.getFullLabel()))
.setMessage(Slic3rLocalization.getString(def.tooltip))
.setPositiveButton(android.R.string.ok, null)
.show();
return true;
});
break;
}
}
public OptionElement(int specialType) {
this.specialType = specialType;
}
public OptionElement(int icon, String title) {
this.icon = icon;
this.title = title;
@@ -836,10 +979,13 @@ public abstract class ProfileListFragment extends Fragment {
optionEl = el;
}
boolean hasSpecialType() {
return optionEl != null && optionEl.specialType != -1;
}
@Override
public View onCreateView(Context ctx) {
FrameLayout v = new FrameLayout(ctx);
v.setBackgroundColor(Color.GREEN);
v.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(32)) {{
bottomMargin = ViewUtils.dp(16);
}});
@@ -847,6 +993,49 @@ public abstract class ProfileListFragment extends Fragment {
}
}
private final static class CloudProfileHeaderView extends LinearLayout implements IThemeView {
private ImageView avatar;
private TextView title;
private TextView subtitle;
private boolean hasAvatar;
public CloudProfileHeaderView(Context context) {
super(context);
setOrientation(HORIZONTAL);
setGravity(Gravity.CENTER_VERTICAL);
setPadding(ViewUtils.dp(21), ViewUtils.dp(16), ViewUtils.dp(21), ViewUtils.dp(16));
avatar = new ImageView(context);
addView(avatar, new LayoutParams(ViewUtils.dp(26), ViewUtils.dp(26)) {{
setMarginEnd(ViewUtils.dp(12));
}});
LinearLayout ll = new LinearLayout(context);
ll.setOrientation(VERTICAL);
ll.setGravity(Gravity.CENTER);
title = new TextView(context);
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
title.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
ll.addView(title);
subtitle = new TextView(context);
subtitle.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
ll.addView(subtitle);
addView(ll, new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
onApplyTheme();
}
@Override
public void onApplyTheme() {
setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 32));
title.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
subtitle.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
if (!hasAvatar) {
avatar.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.textColorSecondary)));
}
}
}
private final static class CategoryHolderView extends LinearLayout implements IThemeView {
private ImageView icon;
private TextView title;
@@ -28,6 +28,7 @@ import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.components.BeamColorPickerPopUp;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.events.BeamServerDataUpdatedEvent;
import ru.ytkab0bp.slicebeam.events.CloudUserInfoUpdatedEvent;
import ru.ytkab0bp.slicebeam.recycler.PreferenceItem;
import ru.ytkab0bp.slicebeam.theme.BeamTheme;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
@@ -45,6 +46,10 @@ public class SettingsFragment extends ProfileListFragment {
@Override
protected List<OptionElement> getConfigItems() {
return Arrays.asList(
BeamServerData.isCloudAvailable() ? new OptionElement(SPECIAL_TYPE_CLOUD_HEADER).setOnClick(() -> {
Activity act = (Activity) getContext();
act.startActivity(new Intent(act, SetupActivity.class).putExtra(SetupActivity.EXTRA_CLOUD_PROFILE, true));
}) : null,
new OptionElement(R.drawable.paint_roller_outline_28, getContext().getString(R.string.SettingsInterface)),
new OptionElement(new PreferenceItem().setTitle(getContext().getString(R.string.SettingsInterfaceTheme)).setValueProvider(() -> getContext().getString(Prefs.getThemeMode().title)).setOnClickListener(v -> {
String[] items = new String[Prefs.ThemeMode.values().length];
@@ -107,7 +112,7 @@ public class SettingsFragment extends ProfileListFragment {
BeamTheme.LIGHT.colors.put(android.R.attr.colorAccent, Prefs.getAccentColor());
BeamTheme.DARK.colors.put(android.R.attr.colorAccent, Prefs.getAccentColor());
ThemesRepo.invalidate((Activity) getContext());
recyclerView.getAdapter().notifyItemChanged(1);
recyclerView.getAdapter().notifyItemChanged(2 - (BeamServerData.isCloudAvailable() ? 0 : 1));
}
})
.setNegativeButtonText(getContext().getString(R.string.SettingsInterfaceColorReset))
@@ -130,7 +135,7 @@ public class SettingsFragment extends ProfileListFragment {
Prefs.setRenderScale(variants[which]);
dialog.dismiss();
// I'm too lazy to calculate real position for now
recyclerView.getAdapter().notifyItemChanged(3);
recyclerView.getAdapter().notifyItemChanged(4 - (BeamServerData.isCloudAvailable() ? 0 : 1));
})
.show();
})),
@@ -144,7 +149,7 @@ public class SettingsFragment extends ProfileListFragment {
}),
BeamServerData.isBoostyAvailable() ? new OptionElement(R.drawable.boosty, getContext().getString(R.string.SettingsBoosty)).setColor(R.attr.boostyColorTop, true).setOnClick(() -> {
Activity act = (Activity) getContext();
act.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://boosty.to/ytkab0bp")));
act.startActivity(new Intent(act, SetupActivity.class).putExtra(SetupActivity.EXTRA_BOOSTY_ONLY, true));
}) : null,
new OptionElement(R.drawable.k3d_logo_new_14, getContext().getString(R.string.SettingsK3D)).setColor(R.attr.k3dColor, true).setOnClick(() -> {
Activity act = (Activity) getContext();
@@ -177,6 +182,13 @@ public class SettingsFragment extends ProfileListFragment {
setConfigItems(getConfigItems());
}
@EventHandler(runOnMainThread = true)
public void onUserInfoUpdated(CloudUserInfoUpdatedEvent e) {
if (BeamServerData.isCloudAvailable()) {
recyclerView.getAdapter().notifyItemChanged(0);
}
}
@Override
protected void cloneCurrentProfile() {}
@@ -107,6 +107,18 @@ public class MobileNavigationDelegate extends DelegateSlotImpl {
return root = fl;
}
@Override
public boolean onBackPressed() {
if (super.onBackPressed()) {
return true;
}
if (currentSlot != 0) {
switchSlot(0, () -> navigationView.setSelectedItemId(0));
return true;
}
return false;
}
@Override
public FrameLayout getOverlayView() {
return root;
@@ -21,6 +21,7 @@ import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.theme.BeamTheme;
import ru.ytkab0bp.slicebeam.theme.IThemeView;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
@@ -30,10 +31,13 @@ public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.Preference
private CharSequence mTitle;
private ValueProvider mSubtitle;
private View.OnClickListener onClickListener;
private View.OnLongClickListener onLongClickListener;
private int textColorRes;
private boolean noTint;
private ValueProvider valueProvider;
private float roundRadius;
private int mPaddings = ViewUtils.dp(12);
private boolean mForceDark;
public PreferenceItem setTitle(CharSequence title) {
mTitle = title;
@@ -45,6 +49,16 @@ public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.Preference
return this;
}
public PreferenceItem setPaddings(int paddings) {
this.mPaddings = paddings;
return this;
}
public PreferenceItem setForceDark(boolean mForceDark) {
this.mForceDark = mForceDark;
return this;
}
public PreferenceItem setSubtitleProvider(ValueProvider mSubtitle) {
this.mSubtitle = mSubtitle;
return this;
@@ -90,6 +104,11 @@ public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.Preference
return this;
}
public PreferenceItem setOnLongClickListener(View.OnLongClickListener onLongClickListener) {
this.onLongClickListener = onLongClickListener;
return this;
}
@Override
public PreferenceHolderView onCreateView(Context ctx) {
return new PreferenceHolderView(ctx);
@@ -106,6 +125,8 @@ public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.Preference
private TextView value;
private float radius;
private PreferenceItem item;
public PreferenceHolderView(Context context) {
super(context);
@@ -159,14 +180,14 @@ public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.Preference
value.setVisibility(GONE);
addView(value, new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
int pad = ViewUtils.dp(12);
setPadding(pad, pad, pad, pad);
setMinimumHeight(ViewUtils.dp(56));
setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
onApplyTheme();
}
void bind(PreferenceItem item) {
this.item = item;
setPadding(item.mPaddings, item.mPaddings, item.mPaddings, item.mPaddings);
title.setText(item.mTitle);
title.setVisibility(TextUtils.isEmpty(item.mTitle) ? GONE : VISIBLE);
@@ -189,6 +210,7 @@ public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.Preference
} else {
setClickable(false);
}
setOnLongClickListener(item.onLongClickListener);
if (item.textColorRes != 0) {
title.setTextColor(ThemesRepo.getColor(item.textColorRes));
@@ -210,15 +232,19 @@ public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.Preference
ViewGroup.LayoutParams params = icon.getLayoutParams();
params.width = params.height = radius != 0 ? ViewUtils.dp(42) : ViewUtils.dp(28);
if (item.mForceDark) {
onApplyTheme();
}
}
@Override
public void onApplyTheme() {
title.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
subtitle.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
value.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
icon.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.textColorSecondary)));
setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
BeamTheme theme = item != null && item.mForceDark ? BeamTheme.DARK : ThemesRepo.getCurrent();
title.setTextColor(theme.colors.get(android.R.attr.textColorPrimary));
subtitle.setTextColor(theme.colors.get(android.R.attr.textColorSecondary));
value.setTextColor(theme.colors.get(android.R.attr.textColorSecondary));
icon.setImageTintList(ColorStateList.valueOf(theme.colors.get(android.R.attr.textColorSecondary)));
setBackground(ViewUtils.createRipple(theme.colors.get(android.R.attr.colorControlHighlight), 16));
}
}
@@ -6,6 +6,7 @@ import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.ImageView;
@@ -28,6 +29,7 @@ public class PreferenceSwitchItem extends SimpleRecyclerItem<PreferenceSwitchIte
private String mKey;
private boolean mDefaultValue;
private CompoundButton.OnCheckedChangeListener mChangeListener;
private View.OnLongClickListener mLongClickListener;
private ValueProvider valueProvider;
public PreferenceSwitchItem setValueProvider(ValueProvider valueProvider) {
@@ -46,6 +48,11 @@ public class PreferenceSwitchItem extends SimpleRecyclerItem<PreferenceSwitchIte
return this;
}
public PreferenceSwitchItem setLongClickListener(View.OnLongClickListener longClickListener) {
mLongClickListener = longClickListener;
return this;
}
public PreferenceSwitchItem setTitle(CharSequence title) {
mTitle = title;
return this;
@@ -89,9 +96,10 @@ public class PreferenceSwitchItem extends SimpleRecyclerItem<PreferenceSwitchIte
icon = new ImageView(context);
icon.setLayoutParams(new LayoutParams(ViewUtils.dp(28), ViewUtils.dp(28)) {{
setMarginEnd(ViewUtils.dp(16));
gravity = Gravity.CENTER_VERTICAL;
setMarginStart(ViewUtils.dp(4));
setMarginEnd(ViewUtils.dp(8));
}});
addView(icon);
LinearLayout innerLayout = new LinearLayout(context);
innerLayout.setOrientation(VERTICAL);
@@ -154,13 +162,14 @@ public class PreferenceSwitchItem extends SimpleRecyclerItem<PreferenceSwitchIte
item.mChangeListener.onCheckedChanged(matSwitch, check);
}
});
setOnLongClickListener(item.mLongClickListener);
}
@Override
public void onApplyTheme() {
title.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
subtitle.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
icon.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.colorAccent)));
icon.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.textColorSecondary)));
setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
}
}
@@ -34,14 +34,14 @@ public class Camera {
}
public void zoom(float zoom) {
this.zoom = MathUtils.clamp(this.zoom + zoom / 25f, 1f, 5f);
this.zoom = MathUtils.clamp(this.zoom + zoom / 25f, 1f, 10f);
}
public void setZoom(float zoom) {
this.zoom = zoom;
}
public void move(float x, float y) {
public Vec3d calcScreenMovement(float x, float y) {
x /= zoom;
y /= zoom;
@@ -61,8 +61,10 @@ public class Camera {
screenX.multiply(x);
screenY.multiply(y);
Vec3d move = new Vec3d(screenX).add(screenY);
return new Vec3d(screenX).add(screenY);
}
public void move(float x, float y) {
Vec3d move = calcScreenMovement(x, y);
position.add(move);
origin.add(move);
}
@@ -39,13 +39,13 @@ public class CoordAxes {
arrow.render();
}
public void render(double[] viewMatrix, double[] projectionMatrix, float emissionFactor, float invZoom) {
public void render(GLShadersManager shadersManager, double[] viewMatrix, double[] projectionMatrix, float emissionFactor, float invZoom) {
if (!arrow.isInitialized()) {
arrow.stilizedArrow(tipRadius, tipLength, stemRadius, stemLength);
}
GLShaderProgram currentShader = GLShadersManager.getCurrentShader();
GLShaderProgram shader = GLShadersManager.get(GLShadersManager.SHADER_GOURAUD_LIGHT);
GLShaderProgram currentShader = shadersManager.getCurrentShader();
GLShaderProgram shader = shadersManager.get(GLShadersManager.SHADER_GOURAUD_LIGHT);
if (currentShader != null) {
currentShader.stopUsing();
}

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