Compare commits

...

33 Commits

Author SHA1 Message Date
Collin Kees
d227b97843 Adjusted com calcs (#769) 2023-07-07 14:16:12 -04:00
Eiren Rain
7f11534fc1 Fix status not working (#765) 2023-07-04 21:26:25 +02:00
Uriel
af2cf10c16 thats weird... 2023-07-04 16:19:58 -03:00
Eiren Rain
055fd78d4d New Pontoon translations (#739) 2023-07-04 19:28:05 +02:00
phoebe
26b472893d Pontoon: Update Turkish (tr) localization of GUI
Co-authored-by: Stupid <dfgfdgrwEHRE@gmail.com>
Co-authored-by: phoebe <brnjyhvmn@gmail.com>
2023-07-04 17:24:55 +00:00
Kamilake
26fbbebb89 Pontoon: Update Korean (ko) localization of GUI
Co-authored-by: Kamilake <exjang0@gmail.com>
2023-07-04 17:24:55 +00:00
FennT
2eba83411c Pontoon: Update Arabic (ar) localization of GUI
Co-authored-by: FennT <0094falcon@gmail.com>
2023-07-04 17:24:55 +00:00
Vyolex
80ffa1a9b4 Pontoon: Update Dutch (nl) localization of GUI
Co-authored-by: Vyolex <25586367+Vyolex@users.noreply.github.com>
2023-07-04 17:24:55 +00:00
Uriel
1b06799315 fix extra space on Norwegian 2023-07-04 17:24:55 +00:00
Erimel
046b0b8be3 Pontoon: Update French (fr) localization of GUI
Co-authored-by: Erimel <loukalemire@gmail.com>
2023-07-04 17:24:55 +00:00
Meow Wei
f4b16f2cdb Pontoon: Update Traditional Chinese (zh-Hant) localization of GUI
Co-authored-by: Meow Wei <medicalwei@gmail.com>
2023-07-04 17:24:55 +00:00
HappyTownCats
b266afedea Pontoon: Update Ukrainian (uk) localization of GUI
Co-authored-by: Uriel <urielfontan2002@gmail.com>
Co-authored-by: HappyTownCats <HappyTownCats@gmail.com>
Co-authored-by: nyarl <nyarl@outlook.com>
2023-07-04 17:24:55 +00:00
Kaiera huzu
646eb94f72 Pontoon: Update Thai (th) localization of GUI
Co-authored-by: Kaiera huzu <patsakorn9090@gmail.com>
2023-07-04 17:24:55 +00:00
Hornil
1bf79fc72d Pontoon: Update Russian (ru) localization of GUI
Co-authored-by: Uriel <urielfontan2002@gmail.com>
Co-authored-by: SummerArtz <summerpzmail@gmail.com>
Co-authored-by: Hornil <vixlyaev@gmail.com>
2023-07-04 17:24:55 +00:00
ReDoX
cddbb93c99 Pontoon: Update Polish (pl) localization of GUI
Co-authored-by: ReDoX <redox01@o2.pl>
2023-07-04 17:24:55 +00:00
beq
5463eee217 Pontoon: Update Japanese (ja) localization of GUI
Co-authored-by: 白貓 <yen.max@gmail.com>
Co-authored-by: beq <beqbdean@gmail.com>
Co-authored-by: 空影/Sorakage <sorakage033@gmail.com>
2023-07-04 17:24:55 +00:00
namakeingo
c93709f80f Pontoon: Update Hebrew (he) localization of GUI
Co-authored-by: namakeingo <namakeingo@gmail.com>
2023-07-04 17:24:55 +00:00
namakeingo
f6b915a88e Pontoon: Update Greek (el) localization of GUI
Co-authored-by: namakeingo <namakeingo@gmail.com>
Co-authored-by: Uriel <urielfontan2002@gmail.com>
Co-authored-by: odydoubouridis <odydoubouridis@gmail.com>
2023-07-04 17:24:55 +00:00
thearxari
9231356638 Pontoon: Update Czech (cs) localization of GUI
Co-authored-by: thearxari <thearxari@gmail.com>
2023-07-04 17:24:55 +00:00
TheDevMinerTV
7bb32a382a Pontoon: Update German (de) localization of GUI
Co-authored-by: TheDevMinerTV <devminer@devminer.xyz>
Co-authored-by: unlogisch <unlogisch@gmx.ch>
Co-authored-by: Zahnatom <zahnatomletsplay@gmail.com>
2023-07-04 17:24:55 +00:00
Disappear9
a17f651f64 Pontoon: Update Simplified Chinese (zh-Hans) localization of GUI
Co-authored-by: Disappear9 <disappear9@outlook.com>
2023-07-04 17:24:55 +00:00
Gaabbu
c9f0f6e27c Pontoon: Update Portuguese (pt-BR) localization of GUI
Co-authored-by: Gaabbu <gabrielsonada@outlook.com.br>
2023-07-04 17:24:55 +00:00
Erimel
3defd47c29 Improve Autobone upperChest and fix VMC upperChest (#754) 2023-07-04 20:24:50 +03:00
Erimel
3068fada17 Don't ask twice to reset non-imu trackers (#760)
Co-authored-by: Uriel <urielfontan2002@gmail.com>
2023-07-04 20:24:16 +03:00
Collin Kees
e71ed5cf6c Acceleration alignment fix (#737)
Co-authored-by: Erimel <marioluigivideo@gmail.com>
Co-authored-by: Butterscotch! <bscotchvanilla@gmail.com>
2023-07-03 19:43:28 -04:00
Erimel
0ccd7e260c Add upper chest tracking point (#716) 2023-07-02 14:33:36 +03:00
lucas lelievre
36c4889d75 mobile body proportions (#718)
Co-authored-by: Uriel <urielfontan2002@gmail.com>
2023-07-01 20:29:31 -04:00
Uriel
8da9e63c45 Fix Dropdown not opening (#744) 2023-07-01 19:59:21 -04:00
Butterscotch!
5e81e3ac4c Make AutoBone more modular and readable (#684) 2023-06-30 00:44:14 +03:00
Uriel
dabb78e545 Fix mobile bugs (#740)
Co-authored-by: lucas lelievre <loucass003@gmail.com>
2023-06-27 00:49:16 +03:00
Evgeniy Zhabotinskiy
582618ee72 Stop unconditionally hogging serial ports (#692) 2023-06-27 00:47:15 +03:00
Erimel
047667432c That's cringe (#742) 2023-06-26 23:10:23 +03:00
Erimel
f4261d5bc2 Clear untranslated keys (#741) 2023-06-24 23:20:40 -04:00
85 changed files with 2744 additions and 1112 deletions

View File

@@ -12,12 +12,17 @@ websocket-connection_lost = انقطع الاتصال بالسيرفر. يتم
## Update notification
version_update-title = نسخة جديدة متوفرة: { $version }
version_update-description = سيؤدي النقر على "{ version_update-update }" إلى تنزيل مثبت SlimeVR نيابة عنك.
version_update-update = تحديث
version_update-close = أغلق
## Tips
tips-find_tracker = لست متأكد من أجهزة التعقب؟ قم بتحريك الجهاز لتحديد العنصر المناسب.
tips-do_not_move_heels = يرجى عدم تحريك كاحليك أثناء التسجيل!
tips-file_select = اسحب الملفات وأفلتها لاستخدامها أو <u> تصفح </ u>
tips-tap_setup = يمكنك النقر ببطء مرتين على جهاز التعقب لاختياره بدلاً من تحديده من القائمة.
## Body parts
@@ -31,6 +36,7 @@ body_part-RIGHT_HAND = اليد اليمنى
body_part-RIGHT_UPPER_LEG = الفخذ الأيمن
body_part-RIGHT_LOWER_LEG = الكاحل الأيمن
body_part-RIGHT_FOOT = القدم اليمنى
body_part-UPPER_CHEST = أعلى الصدر
body_part-CHEST = الصدر
body_part-WAIST = الخصر
body_part-HIP = الورك
@@ -48,8 +54,9 @@ skeleton_bone-NONE = غير محدد
skeleton_bone-HEAD = إمالة الرأس
skeleton_bone-NECK = طول العنق
skeleton_bone-torso_group = طول الجذع
skeleton_bone-CHEST = طول الصدر
skeleton_bone-UPPER_CHEST = طول أعلى الصدر
skeleton_bone-CHEST_OFFSET = درجة تشريد الصدر
skeleton_bone-CHEST = طول الصدر
skeleton_bone-WAIST = طول الخصر
skeleton_bone-HIP = طول الورك
skeleton_bone-HIP_OFFSET = درجة تشريد الورك
@@ -102,6 +109,8 @@ bvh-recording = تسجيل...
## Tracking pause
tracking-unpaused = إيقاف التعقب مؤقتا
tracking-paused = إلغاء الإيقاف التعقب
## Widget: Overlay settings
@@ -168,6 +177,9 @@ tracker-infos-custom_name = اسم مخصص
tracker-infos-url = عنوان URL لجهاز التعقب
tracker-infos-version = إصدار البرنامج الثابت
tracker-infos-hardware_rev = مراجعة الأجهزة
tracker-infos-hardware_identifier = معرف الجهاز
tracker-infos-imu = مستشعر IMU
tracker-infos-board_type = اللوحة الرئيسية
## Tracker settings
@@ -220,6 +232,7 @@ tracker_selection_menu-RIGHT_UPPER_LEG = { -tracker_selection-part } الفخذ
tracker_selection_menu-RIGHT_LOWER_LEG = { -tracker_selection-part } الكاحل الأيمن؟
tracker_selection_menu-RIGHT_FOOT = { -tracker_selection-part } القدم اليمنى؟
tracker_selection_menu-RIGHT_CONTROLLER = { -tracker_selection-part } وحدة التحكم اليمنى؟
tracker_selection_menu-UPPER_CHEST = { -tracker_selection-part } أعلى الصدر؟
tracker_selection_menu-CHEST = { -tracker_selection-part } الصدر؟
tracker_selection_menu-WAIST = { -tracker_selection-part } الخصر؟
tracker_selection_menu-HIP = { -tracker_selection-part } الورك؟
@@ -521,7 +534,26 @@ onboarding-wifi_creds-password =
onboarding-reset_tutorial-back = العودة إلى معايرة التركيب
onboarding-reset_tutorial = إعادة البرنامج التعليمي
onboarding-reset_tutorial-description = هذه الميزة لم تنته بعد، فقط اضغط على متابعة
onboarding-reset_tutorial-explanation = أثناء استخدام أجهزة التعقب، قد تخرج عن المحاذاة بسبب انحراف IMU ، أو لأنك ربما تكون قد نقلتها جسديا. لديك عدة طرق لإصلاح هذا.
onboarding-reset_tutorial-skip = تخطى الخطوة
# Cares about multiline
onboarding-reset_tutorial-0 =
اضغط على جهاز التعقب المحدد { $taps } مرات لتشغيل إعادة ضبط الانعراج.
سيؤدي ذلك إلى جعل أجهزة التعقب تواجه نفس اتجاه HMD الخاص بك.
# Cares about multiline
onboarding-reset_tutorial-1 =
اضغط على جهاز التعقب المحدد { $taps } مرات لتشغيل إعادة تعيين كاملة.
يجب أن تكون واقفًا (i-pose). هناك تأخير لمدة 3 ثوان (قابل للتكوين) قبل إعادة التعيين بالكامل.
هذا يعيد تعيين موضع ودوران جميع جهاز التعقب. يجب أن يحل معظم المشاكل.
# Cares about multiline
onboarding-reset_tutorial-2 =
اضغط على المتتبع المحدد { $taps } مرات لتشغيل إعادة تعيين متصاعد.
يساعد إعادة التعيين المتصاعد في تحديد كيفية وضع أجهزة التعقب عليك بالفعل. لذلك إذا قمت بنقلهم عن طريق الخطأ وغيرت كيفية توجيههم بمقدار كبير ، فسيساعد ذلك.
يجب أن تكون في وضع تزلج كما هو موضح في معالج "التثبيت التلقائي" ولديك تأخير لمدة 3 ثوانٍ (قابل للتكوين) قبل أن يتم تشغيله.
## Setup start
@@ -576,9 +608,24 @@ onboarding-connect_tracker-next = لقد قمت بتوصيل جميع أجهزة
## Tracker calibration tutorial
onboarding-calibration_tutorial = برنامج تعليم معايرة IMU
onboarding-calibration_tutorial-subtitle = سوف يساعد هذا في تقليل الانجراف التعقب!
onboarding-calibration_tutorial-description = كل مرة تقوم بتشغيل أجهزة التعقب، يجب أن تستريح للحظة على سطح مستوٍ للمعايرة. لنفعل الشيء نفسه بالنقر فوق الزر "{ onboarding-calibration_tutorial-calibrate }" ، <b>لا تحركها!</b>
onboarding-calibration_tutorial-calibrate = وضعت أجهزة التعقب على الطاولة
onboarding-calibration_tutorial-status-waiting = بانتظارك
onboarding-calibration_tutorial-status-calibrating = جاري المعايرة
onboarding-calibration_tutorial-status-success = رائع!
onboarding-calibration_tutorial-status-error = تم نقل جهاز التعقب
## Tracker assignment tutorial
onboarding-assignment_tutorial = كيفية تحضير جهاز تعقب Slime قبل وضعه
onboarding-assignment_tutorial-first_step = 1. ضع ملصق جزء الجسم (إذا كان لديك واحد) على جهاز التعقب وفقا لاختيارك
# This text has a character limit of around 11 characters, so please keep it short
onboarding-assignment_tutorial-sticker = ملصق
onboarding-assignment_tutorial-second_step = 2. قم بتوصيل الشريط بجهاز التعقب الخاص بك ، مع الحفاظ على جانب الخطاف والحلقة من وجه الشريط في الاتجاه التالي:
onboarding-assignment_tutorial-second_step-continuation = يجب أن يكون جانب الخطاف والحلقة للامتداد في هذا الاتجاه:
onboarding-assignment_tutorial-done = وضعت الملصقات والأشرطة!
## Tracker assignment setup
@@ -589,10 +636,12 @@ onboarding-assign_trackers-description = دعنا نختار موقع أجهزة
# $assigned (Number) - Trackers that have been assigned a body part
# $trackers (Number) - Trackers connected to the server
onboarding-assign_trackers-assigned =
{ $assigned ->
{ $trackers ->
[zero] { $assigned } من { $trackers } أجهزة تعقب عينت
[one] جهاز واحد من { $trackers } أجهزة تعقب عينت
[two] جهازان من { $trackers } أجهزة تعقب عينت
[few] { $assigned } من { $trackers } أجهزة تعقب عينت
[many] { $assigned } من { $trackers } أجهزة تعقب عينت
*[other] { $assigned } من { $trackers } أجهزة تعقب عينت
}
onboarding-assign_trackers-advanced = إظهار مواقع التعيين المتقدمة
@@ -669,13 +718,15 @@ onboarding-assign_trackers-warning-WAIST =
## Tracker mounting method choose
onboarding-choose_mounting = ما طريقة معايرة التركيب المستخدمة؟
# Multiline text
onboarding-choose_mounting-description = اتجاه التركيب يصحح وضع أجهزة التعقب على جسمك.
onboarding-choose_mounting-auto_mounting = التركيب التلقائي
# Italized text
onboarding-choose_mounting-auto_mounting-subtitle = الموصى به
onboarding-choose_mounting-auto_mounting-label = تجريبي
onboarding-choose_mounting-auto_mounting-description = سيكتشف هذا تلقائيًا اتجاهات التركيب لجميع أجهزة التعقب من وضعين
onboarding-choose_mounting-manual_mounting = التركيب اليدوي
# Italized text
onboarding-choose_mounting-manual_mounting-subtitle = إذا كنت تعلم ماذا تفعل
onboarding-choose_mounting-manual_mounting-label = المستحسن
onboarding-choose_mounting-manual_mounting-description = سيسمح لك باختيار اتجاه التثبيت يدويًا لكل جهاز تعقب
## Tracker manual mounting setup
@@ -710,6 +761,10 @@ onboarding-automatic_mounting-put_trackers_on-next = ارتديت جميع أج
## Tracker proportions method choose
onboarding-choose_proportions = ما هي طريقة معايرة النسب التي يجب استخدامها؟
# Multiline string
onboarding-choose_proportions-description =
تستخدم نسب الجسم لمعرفة قياسات جسمك. هم مطلوبون لحساب مواقع أجهزة التعقب.
عندما لا تتطابق نسب جسمك مع تلك المحفوظة ، ستكون دقة التتبع لديك أسوأ وستلاحظ أشياء مثل التزلج أو الانزلاق ، أو أن جسمك لا يتطابق مع الصورة الرمزية بشكل جيد.
onboarding-choose_proportions-auto_proportions = النسب التلقائية
# Italized text
onboarding-choose_proportions-auto_proportions-subtitle = الموصى به
@@ -718,6 +773,8 @@ onboarding-choose_proportions-manual_proportions = النسب اليدوية
# Italized text
onboarding-choose_proportions-manual_proportions-subtitle = للمسات الصغيرة
onboarding-choose_proportions-manual_proportions-description = سيسمح لك بتعديل النسب يدويًا عن طريق تعديلها مباشرة
onboarding-choose_proportions-export = تصدير النسب
onboarding-choose_proportions-file_type = ملف نسب الجسم
## Tracker manual proportions setup
@@ -787,3 +844,10 @@ home-no_trackers = لم يتم الكشف أو تعيين عن أي جهاز ت
## Status system
status_system-StatusTrackerReset = يوصى بإجراء إعادة تعيين كاملة نظرًا لعدم تعديل واحد أو أكثر من أجهزة التعقب.
status_system-StatusSteamVRDisconnected =
{ $type ->
[steamvr_feeder] حاليًا غير متصل بتطبيق SlimeVR Feeder.
*[other] حاليًا غير متصل بـ SteamVR عبر برنامج تشغيل SlimeVR.
}
status_system-StatusTrackerError = يحتوي جهاز التعقب { $trackerName } على خطأ.

View File

@@ -1,6 +1,3 @@
### SlimeVR complete GUI translations
# Please developers (not translators) don't reuse a key inside another key
# or concat text with a translation string in the code, use the appropriate
# features like variables and selectors in each appropriate case!
@@ -13,6 +10,9 @@
websocket-connecting = Připojování k serveru
websocket-connection_lost = Ztráta spojení se serverem. Pokus o obnovení připojení...
## Update notification
## Tips
tips-find_tracker = Nejste si jisti, který tracker je který? Zatřeste tracker a zvýrazní se odpovídající položka.
@@ -30,7 +30,6 @@ body_part-RIGHT_HAND = Pravá ruka
body_part-RIGHT_UPPER_LEG = Pravé stehno
body_part-RIGHT_LOWER_LEG = Pravý kotník
body_part-RIGHT_FOOT = Pravá noha
body_part-RIGHT_CONTROLLER = Pravý ovladač
body_part-CHEST = Hrudník
body_part-WAIST = Pás
body_part-HIP = Kyčel
@@ -41,7 +40,6 @@ body_part-LEFT_HAND = Levá ruka
body_part-LEFT_UPPER_LEG = Levé stehno
body_part-LEFT_LOWER_LEG = Levý kotník
body_part-LEFT_FOOT = Levá noha
body_part-LEFT_CONTROLLER = Levý ovladač
## Proportions
@@ -63,8 +61,6 @@ skeleton_bone-SHOULDERS_DISTANCE = Vzdálenost ramen
skeleton_bone-SHOULDERS_WIDTH = Šířka ramen
skeleton_bone-UPPER_ARM = Délka nadloktí
skeleton_bone-LOWER_ARM = Délka podloktí
skeleton_bone-CONTROLLER_Y = Vzdálenost ovladače Y
skeleton_bone-CONTROLLER_Z = Vzdálenost ovladače Z
skeleton_bone-ELBOW_OFFSET = Odsazení loktů
## Tracker reset buttons
@@ -72,7 +68,6 @@ skeleton_bone-ELBOW_OFFSET = Odsazení loktů
reset-reset_all = Obnovení všech proporcí
reset-full = Resetovat
reset-mounting = Obnovit montáž
reset-quick = Rychlý reset
## Serial detection stuff
@@ -93,11 +88,16 @@ navbar-mounting = Montážní kalibrace
navbar-onboarding = Průvodce nastavením
navbar-settings = Nastavení
## Bounding volume hierarchy recording
## Biovision hierarchy recording
bvh-start_recording = Nahrávat BVH
bvh-recording = Nahrávání...
## Tracking pause
tracking-unpaused = Pozastavit sledování
tracking-paused = Pokračovat se sledováním
## Widget: Overlay settings
widget-overlay = Překrytí
@@ -124,6 +124,7 @@ widget-developer_mode-more_info = Více informací
widget-imu_visualizer = Rotace
widget-imu_visualizer-rotation_raw = Nezpracované
widget-imu_visualizer-rotation_preview = Náhled
widget-imu_visualizer-rotation_hide = Skrýt
## Tracker status
@@ -160,6 +161,9 @@ tracker-infos-manufacturer = Výrobce
tracker-infos-display_name = Zobrazovaný název
tracker-infos-custom_name = Vlastní název
tracker-infos-url = URL Trackeru
tracker-infos-version = Verze firmwaru
tracker-infos-hardware_rev = Revize hardwaru
tracker-infos-board_type = Základní deska
## Tracker settings
@@ -297,8 +301,6 @@ settings-general-tracker_mechanics-drift_compensation-max_resets-label = Použí
## FK/Tracking settings
settings-general-fk_settings = Nastavení trackování
settings-general-fk_settings-leg_tweak = Vyladění nohou
settings-general-fk_settings-leg_tweak-description = Podlahovej-clip může snížit nebo dokonce eliminovat klipování s podlahou, ale může způsobit problémy, když klečíte na kolenou. Korekce-bruslení opravuje bruslení na ledě, avšak může snížit přesnost některých pohybových vzorců.
# Floor clip:
# why the name - came from the idea of noclip in video games, but is the opposite where clipping to the floor is a desired feature
# definition - Prevents the foot trackers from going lower than they where when a reset was performed
@@ -334,12 +336,6 @@ settings-general-gesture_control-taps =
[few] { $amount } klepnutí
*[other] { $amount } klepnutí
}
settings-general-gesture_control-quickResetEnabled = Povolit klepnutí pro rychlý resetování
settings-general-gesture_control-quickResetDelay = Zpoždění rychlého resetu
settings-general-gesture_control-quickResetTaps = Klepnutí pro rychlý reset
settings-general-gesture_control-resetEnabled = Povolit klepnutí pro resetování
settings-general-gesture_control-resetDelay = Zpoždění resetování
settings-general-gesture_control-resetTaps = Klepnutí pro resetování
settings-general-gesture_control-mountingResetEnabled = Povolit klepnutí pro resetování montáže
settings-general-gesture_control-mountingResetDelay = Zpoždění resetování montáže
settings-general-gesture_control-mountingResetTaps = Klepnutí pro resetování montáže
@@ -427,11 +423,14 @@ settings-osc-vrchat-network-address-placeholder = VRChat ip adresa
settings-osc-vrchat-network-trackers = Trackery
settings-osc-vrchat-network-trackers-description = Vypnuti a zapnutí odesílání konkrétních trackerů přes OSC.
settings-osc-vrchat-network-trackers-chest = Hrudník
settings-osc-vrchat-network-trackers-waist = Pás
settings-osc-vrchat-network-trackers-hip = Kyčel
settings-osc-vrchat-network-trackers-knees = Kolena
settings-osc-vrchat-network-trackers-feet = Chodidla
settings-osc-vrchat-network-trackers-elbows = Lokty
## VMC OSC settings
## Setup/onboarding menu
onboarding-skip = Přeskočit nastavení
@@ -459,15 +458,10 @@ onboarding-wifi_creds-password =
onboarding-reset_tutorial-back = Zpět na kalibraci montáže
onboarding-reset_tutorial = Obnovit tutoriál
onboarding-reset_tutorial-description = Tato funkce není dokončena, stačí stisknout tlačítko pokračovat
## Setup start
onboarding-home = Vítejte k SlimeVR
# This cares about multilines and it's centered!!
onboarding-home-description =
Přinášíme full-body tracking
pro každého
onboarding-home-start = Pusťme se do toho!
## Enter VR part of setup
@@ -492,10 +486,7 @@ onboarding-connect_tracker-description-p1 = Všechny zatím nepřipojené jednod
onboarding-connect_tracker-issue-serial = Mám potíže s připojením!
onboarding-connect_tracker-usb = USB Tracker
onboarding-connect_tracker-connection_status-connecting = Odesílání přihlašovacích údajů Wi-Fi
onboarding-connect_tracker-connection_status-connected = Připojeno k Wi-Fi
onboarding-connect_tracker-connection_status-error = Nelze se připojit k Wi-Fi
onboarding-connect_tracker-connection_status-start_connecting = Hledání trackerů
onboarding-connect_tracker-connection_status-handshake = Připojeno k Serveru
onboarding-connect_tracker-connection_status-connection_error = Nelze se připojit k síti Wi-Fi
# $amount (Number) - Amount of trackers connected (this is a number, but you can use CLDR plural rules for your language)
# More info on https://www.unicode.org/cldr/cldr-aux/charts/22/supplemental/language_plural_rules.html
# English in this case only has 2 plural rules, which are "one" and "other",
@@ -510,6 +501,13 @@ onboarding-connect_tracker-connected_trackers =
}
onboarding-connect_tracker-next = Připojil jsem všechny své trackery
## Tracker calibration tutorial
onboarding-calibration_tutorial-status-success = Super!
## Tracker assignment tutorial
## Tracker assignment setup
onboarding-assign_trackers-back = Zpět na přihlašovací údaje Wi-Fi
@@ -527,6 +525,12 @@ onboarding-assign_trackers-assigned =
onboarding-assign_trackers-advanced = Zobrazit pokročilá místa přiřazení
onboarding-assign_trackers-next = Přiřadil jsem všechny trackery
## Tracker assignment warnings
## Tracker mounting method choose
## Tracker manual mounting setup
onboarding-manual_mounting-back = Zpět na Enter VR
@@ -556,6 +560,9 @@ onboarding-automatic_mounting-put_trackers_on-title = Nasaďte si trackery
onboarding-automatic_mounting-put_trackers_on-description = Pro kalibraci rotace montáže použijeme právě přiřazené trackery. Nasaďte všechny trackery, na obrázku vpravo vidíte, které jsou které.
onboarding-automatic_mounting-put_trackers_on-next = Mám nasazené všechny trackery
## Tracker proportions method choose
## Tracker manual proportions setup
onboarding-manual_proportions-back = Zpět na Reset tutoriál
@@ -573,22 +580,14 @@ onboarding-automatic_proportions-prev_step = Předchozí krok
onboarding-automatic_proportions-put_trackers_on-title = Nasaďte si trackery
onboarding-automatic_proportions-put_trackers_on-description = Pro kalibraci proporcí použijeme trackery, které jste právě přiřadili. Nasaďte si trackery, na obrázku vpravo vidíte, která jsou která.
onboarding-automatic_proportions-put_trackers_on-next = Mám nasazené všechny trackery
onboarding-automatic_proportions-preparation-title = Příprava
onboarding-automatic_proportions-preparation-description = Umístěte židli přímo za sebe do herního prostoru. Buďte připraveni se během nastavení autobonu posadit.
onboarding-automatic_proportions-preparation-next = Jsem před židlí
onboarding-automatic_proportions-start_recording-title = Připravte se hýbat
onboarding-automatic_proportions-start_recording-description = Nyní budeme nahrávat některé konkrétní pózy a pohyby. Tyto se zobrazí na další obrazovce. Po stisknutí tlačítka buďte připraveni začít!
onboarding-automatic_proportions-start_recording-next = Spustit nahrávání
onboarding-automatic_proportions-recording-title = ZÁZN
onboarding-automatic_proportions-recording-description-p0 = Probíhá nahrávání...
onboarding-automatic_proportions-recording-description-p1 = Proveďte níže uvedené pohyby:
onboarding-automatic_proportions-recording-steps-0 = Párkrát ohněte kolena.
onboarding-automatic_proportions-recording-steps-1 = Posaďte se na židli a pak se postavte.
onboarding-automatic_proportions-recording-steps-2 = Natočte horní část těla doleva a pak se ohněte doprava.
onboarding-automatic_proportions-recording-steps-3 = Natočte horní část těla doprava a pak se ohněte doleva.
onboarding-automatic_proportions-recording-steps-4 = Hýbejte se, dokud časovač neskončí.
onboarding-automatic_proportions-recording-processing = Zpracování výsledku
# $time (Number) - Seconds left for the automatic calibration recording to finish (max 15)
# $time (Number) - Seconds left for the automatic calibration recording to finish (max 20)
onboarding-automatic_proportions-recording-timer =
{ $time ->
[one] Zbývá 1 sekunda
@@ -607,3 +606,6 @@ onboarding-automatic_proportions-done-description = Kalibrace proporcí vašeho
## Home
home-no_trackers = Nebyly zjištěny ani přiřazeny žádné trackery
## Status system

View File

@@ -61,7 +61,7 @@ skeleton_bone-HIP_OFFSET = Hüftversatz
skeleton_bone-HIPS_WIDTH = Hüftbreite
skeleton_bone-leg_group = Beinlänge
skeleton_bone-UPPER_LEG = Linker Oberschenkellänge
skeleton_bone-LOWER_LEG = Linker Unterschenkellänge
skeleton_bone-LOWER_LEG = Unterschenkellänge
skeleton_bone-FOOT_LENGTH = Fußlänge
skeleton_bone-FOOT_SHIFT = Fußverschiebung
skeleton_bone-SKELETON_OFFSET = Skelettversatz
@@ -328,10 +328,10 @@ settings-general-fk_settings-leg_tweak-skating_correction = Rutschkorrektur
settings-general-fk_settings-leg_tweak-toe_snap = Zehenausrichtung
settings-general-fk_settings-leg_tweak-foot_plant = Fußkorrektur
settings-general-fk_settings-leg_tweak-skating_correction-amount = Rutschkorrekturstärke
settings-general-fk_settings-leg_tweak-skating_correction-description = Die Rutschkorrektur korrigiert das Wegrutschen des Fußes, kann aber die Genauigkeit bestimmter Bewegungsmuster verringern. Wenn du dies aktivierst, stellen sicher, dass du im Spiel dein Tracking vollständig zurücksetzt und neu kalibrierst.
settings-general-fk_settings-leg_tweak-floor_clip-description = Bodenclip kann das Clipping durch den Boden reduzieren oder sogar eliminieren. Wenn du dies aktivierst, stelle sicher, dass du im Spiel dein Tracking vollständig zurücksetzt und neu kalibrierst.
settings-general-fk_settings-leg_tweak-toe_snap-description = Zehen-Ausrichtung versucht, die Rotation deiner Füße zu erraten, wenn keine Fuß-Tracker verwendet werden.
settings-general-fk_settings-leg_tweak-foot_plant-description = Fußkorrektur richtet deine Füße parallel zum Boden aus, wenn sie den Boden berühren.
settings-general-fk_settings-leg_tweak-skating_correction-description = Die Rutschkorrektur korrigiert das Wegrutschen des Fußes, kann aber die Genauigkeit bestimmter Bewegungsmuster verringern. Wenn Sie dies aktivieren, stellen Sie sicher, dass Sie im Spiel Ihr Tracking vollständig zurücksetzten und neu kalibrieren.
settings-general-fk_settings-leg_tweak-floor_clip-description = Bodenclip kann das Clipping durch den Boden reduzieren oder sogar eliminieren. Wenn Sie dies aktivieren, stellen Sie sicher, dass Sie im Spiel Ihr Tracking vollständig zurücksetzten und neu kalibrieren.
settings-general-fk_settings-leg_tweak-toe_snap-description = Zehen-Ausrichtung versucht, die Rotation Ihrer Füße zu erraten, wenn keine Fuß-Tracker verwendet werden.
settings-general-fk_settings-leg_tweak-foot_plant-description = Fußkorrektur richtet Ihre Füße parallel zum Boden aus, wenn sie den Boden berühren.
settings-general-fk_settings-leg_fk = Beintracking
settings-general-fk_settings-arm_fk = Arm-Tracking
settings-general-fk_settings-arm_fk-description = Ändern Sie die Art und Weise, wie die Arme berechnet werden.
@@ -364,7 +364,7 @@ settings-general-gesture_control-fullResetEnabled = Vollständiger Reset durch A
settings-general-gesture_control-fullResetDelay = Verzögerung für einen vollständigen Reset
settings-general-gesture_control-fullResetTaps = Antipp-Anzahl für einen vollständigen Reset
settings-general-gesture_control-mountingResetEnabled = Antippen für Befestigungs-Reset
settings-general-gesture_control-mountingResetDelay = Befestigungs-Reset-Verzügerung
settings-general-gesture_control-mountingResetDelay = Befestigungs-Reset-Verzögerung
settings-general-gesture_control-mountingResetTaps = Anzahl für Befestigungs-Reset
## Interface settings
@@ -522,7 +522,7 @@ onboarding-wifi_creds-password =
onboarding-reset_tutorial-back = Zurück zur Trackerausrichtung
onboarding-reset_tutorial = Tutorial neustarten
onboarding-reset_tutorial-description = Diese Funktion ist noch nicht fertig, drücken Sie einfach auf Fortsetzen
onboarding-reset_tutorial-explanation = Während Sie Ihre Tracker verwenden, können sie aufgrund der IMU-Gierdrift oder weil Sie sie physisch bewegt haben, aus der Ausrichtung geraten. Sie haben mehrere Möglichkeiten, dies zu beheben.
onboarding-reset_tutorial-skip = Schritt überspringen
# Cares about multiline
onboarding-reset_tutorial-0 =
@@ -694,11 +694,11 @@ onboarding-choose_mounting = Welche Kalibrierungsmethode ist zu verwenden?
onboarding-choose_mounting-description = Die Montageausrichtung korrigiert die Platzierung von Trackern am Körper.
onboarding-choose_mounting-auto_mounting = Befestigung automatisch ermitteln
# Italized text
onboarding-choose_mounting-auto_mounting-subtitle = Empfohlen
onboarding-choose_mounting-auto_mounting-description = Dadurch werden die Befestigungsausrichtungen für alle deine Tracker automatisch aus 2 Posen erkannt
onboarding-choose_mounting-auto_mounting-label = Experimentell
onboarding-choose_mounting-auto_mounting-description = Dadurch werden die Befestigungsausrichtungen für alle Ihrer Tracker automatisch aus 2 Posen erkannt
onboarding-choose_mounting-manual_mounting = Manuelle Befestigungsposition
# Italized text
onboarding-choose_mounting-manual_mounting-subtitle = Wenn du weißt, was du tust
onboarding-choose_mounting-manual_mounting-label = Empfohlen
onboarding-choose_mounting-manual_mounting-description = Auf diese Weise können Sie die Montagerichtung für jeden Tracker manuell auswählen
## Tracker manual mounting setup
@@ -720,7 +720,7 @@ onboarding-automatic_mounting-prev_step = Vorheriger Schritt
onboarding-automatic_mounting-done-title = Tracker Rotation kalibriert.
onboarding-automatic_mounting-done-description = Ihre Rotations-Kalibrierung ist abgeschlossen!
onboarding-automatic_mounting-done-restart = Zurück zum Start
onboarding-automatic_mounting-mounting_reset-title = Drehungs-Reset
onboarding-automatic_mounting-mounting_reset-title = Befestigungs-Reset
onboarding-automatic_mounting-mounting_reset-step-0 = 1. Beugen Sie sich in die "Skifahren"-Pose mit gebeugten Beinen, geneigtem Oberkörper und gebeugten Armen.
onboarding-automatic_mounting-mounting_reset-step-1 = 2. Drücken Sie die Schaltfläche "Befestigungs-Reset" und warten Sie 3 Sekunden, bevor die Drehungen der Tracker gesetzt werden.
onboarding-automatic_mounting-preparation-title = Vorbereitung
@@ -739,7 +739,7 @@ onboarding-choose_proportions-auto_proportions-subtitle = Empfohlen
onboarding-choose_proportions-auto_proportions-description = Dadurch werden Ihre Proportionen erraten, indem Stichproben Ihrer Bewegungen verrechnet werden
onboarding-choose_proportions-manual_proportions = Manuelle Körperproportionen
onboarding-choose_proportions-manual_proportions-description = Auf diese Weise können Sie Ihre Proportionen manuell anpassen, indem Sie diese direkt ändern
onboarding-choose_proportions-save = Proportionen speichern
onboarding-choose_proportions-export = Proportionen exportieren
onboarding-choose_proportions-file_type = Körperproportions-Datei
## Tracker manual proportions setup

View File

@@ -1,6 +1,3 @@
### SlimeVR complete GUI translations
# Please developers (not translators) don't reuse a key inside another key
# or concat text with a translation string in the code, use the appropriate
# features like variables and selectors in each appropriate case!
@@ -13,6 +10,9 @@
websocket-connecting = Σύνδεση με τον διακομιστή
websocket-connection_lost = Η σύνδεση μεταξύ του διακομιστή χάθηκε. Προσπαθώντας να επανασυνδεθεί...
## Update notification
## Tips
tips-find_tracker = Δεν είστε σίγουροι ποιος ανιχνευτής είναι ποιος; Κουνήστε έναν ανιχνευτή και θα επισημάνει το αντίστοιχο στοιχείο.
@@ -32,7 +32,7 @@ body_part-RIGHT_LOWER_LEG = Δεξιός αστράγαλος
body_part-RIGHT_FOOT = Δεξί πόδι
body_part-CHEST = Στήθος
body_part-WAIST = Μέση
body_part-HIP = γοφοί
body_part-HIP = Γοφοί
body_part-LEFT_SHOULDER = Αριστερός ώμος
body_part-LEFT_UPPER_ARM = Αριστερό μπράτσο
body_part-LEFT_LOWER_ARM = Αριστερό αγγόνας
@@ -68,7 +68,6 @@ skeleton_bone-ELBOW_OFFSET = Μετατόπιση αγκώνα
reset-reset_all = Επαναφορά όλων των αναλογιών
reset-full = Επαναφορά
reset-mounting = Επαναφορά τοποθέτησης
reset-quick = Γρήγορη επαναφορά
## Serial detection stuff
@@ -89,11 +88,14 @@ navbar-mounting = Βαθμονόμηση τοποθέτησης
navbar-onboarding = Οδηγός εγκατάστασης
navbar-settings = Ρυθμίσεις
## Bounding volume hierarchy recording
## Biovision hierarchy recording
bvh-start_recording = Εγγραφή BVH
bvh-recording = Γίνεται εγγραφή...
## Tracking pause
## Widget: Overlay settings
widget-overlay = Υπέρθεση
@@ -191,7 +193,32 @@ body_assignment_menu-unassign_tracker = Μη εκχωρημένος ανιχνε
## Tracker assignment menu
# A -translation_key (with a dash in the front) means that it's a label.
# It can only be used in the translation file, it's nice for reusing names and that kind of stuff.
#
# We are using it here because english doesn't require changing the text in each case but
# maybe your language does.
-tracker_selection-part = Ποιος ιχνηλάτης πρέπει να αντιστοιχίσετε στο σας
tracker_selection_menu-NONE = Ποιος ανιχνευτή θέλετε να είναι μη εκχωρημένος;
tracker_selection_menu-HEAD = { -tracker_selection-part } κεφάλι;
tracker_selection_menu-NECK = { -tracker_selection-part } λαιμός;
tracker_selection_menu-RIGHT_SHOULDER = { -tracker_selection-part } δεξιός ώμος;
tracker_selection_menu-RIGHT_UPPER_ARM = { -tracker_selection-part } δεξιό μπράτσο?
tracker_selection_menu-RIGHT_LOWER_ARM = { -tracker_selection-part } δεξί αγγόνας?
tracker_selection_menu-RIGHT_HAND = { -tracker_selection-part } δεξί χέρι?
tracker_selection_menu-RIGHT_UPPER_LEG = { -tracker_selection-part } δεξιός μηρός?
tracker_selection_menu-RIGHT_LOWER_LEG = { -tracker_selection-part } δεξιός αστράγαλος?
tracker_selection_menu-RIGHT_FOOT = { -tracker_selection-part } δεξί πόδι?
tracker_selection_menu-CHEST = { -tracker_selection-part } στήθος?
tracker_selection_menu-WAIST = { -tracker_selection-part } μέση?
tracker_selection_menu-HIP = { -tracker_selection-part } γοφοί?
tracker_selection_menu-LEFT_SHOULDER = { -tracker_selection-part } αριστερός ώμος?
tracker_selection_menu-LEFT_UPPER_ARM = { -tracker_selection-part } αριστερό μπράτσο?
tracker_selection_menu-LEFT_LOWER_ARM = { -tracker_selection-part } αριστερό αγγόνας?
tracker_selection_menu-LEFT_HAND = { -tracker_selection-part } αριστερό χέρι?
tracker_selection_menu-LEFT_UPPER_LEG = { -tracker_selection-part } αριστερός μηρός?
tracker_selection_menu-LEFT_LOWER_LEG = { -tracker_selection-part } αριστερός αστράγαλος?
tracker_selection_menu-LEFT_FOOT = { -tracker_selection-part } αριστερό πόδι?
## Mounting menu
@@ -201,6 +228,11 @@ tracker_selection_menu-NONE = Ποιος ανιχνευτή θέλετε να ε
## SteamVR settings
settings-general-steamvr = SteamVR
settings-general-steamvr-trackers-waist = Μέση
settings-general-steamvr-trackers-chest = Στήθος
settings-general-steamvr-trackers-feet = Πόδια
settings-general-steamvr-trackers-hands = Χέρια
## Tracker mechanics
@@ -222,6 +254,11 @@ tracker_selection_menu-NONE = Ποιος ανιχνευτή θέλετε να ε
## OSC VRChat settings
settings-osc-vrchat-network-trackers-chest = Στήθος
settings-osc-vrchat-network-trackers-feet = Πόδια
## VMC OSC settings
## Setup/onboarding menu
@@ -244,18 +281,30 @@ tracker_selection_menu-NONE = Ποιος ανιχνευτή θέλετε να ε
## Tracker connection setup
## Tracker calibration tutorial
## Tracker assignment tutorial
## Tracker assignment setup
## Tracker assignment warnings
## Tracker mounting method choose
## Tracker manual mounting setup
## Tracker automatic mounting setup
## Tracker proportions method choose
## Tracker manual proportions setup
@@ -264,3 +313,6 @@ tracker_selection_menu-NONE = Ποιος ανιχνευτή θέλετε να ε
## Home
## Status system

View File

@@ -31,6 +31,7 @@ body_part-RIGHT_HAND = Right hand
body_part-RIGHT_UPPER_LEG = Right thigh
body_part-RIGHT_LOWER_LEG = Right ankle
body_part-RIGHT_FOOT = Right foot
body_part-UPPER_CHEST = Upper chest
body_part-CHEST = Chest
body_part-WAIST = Waist
body_part-HIP = Hip
@@ -47,8 +48,9 @@ skeleton_bone-NONE = None
skeleton_bone-HEAD = Head Shift
skeleton_bone-NECK = Neck Length
skeleton_bone-torso_group = Torso length
skeleton_bone-CHEST = Chest Length
skeleton_bone-UPPER_CHEST = Upper Chest Length
skeleton_bone-CHEST_OFFSET = Chest Offset
skeleton_bone-CHEST = Chest Length
skeleton_bone-WAIST = Waist Length
skeleton_bone-HIP = Hip Length
skeleton_bone-HIP_OFFSET = Hip Offset
@@ -207,6 +209,7 @@ tracker_selection_menu-RIGHT_UPPER_LEG = { -tracker_selection-part } right thigh
tracker_selection_menu-RIGHT_LOWER_LEG = { -tracker_selection-part } right ankle?
tracker_selection_menu-RIGHT_FOOT = { -tracker_selection-part } right foot?
tracker_selection_menu-RIGHT_CONTROLLER = { -tracker_selection-part } right controller?
tracker_selection_menu-UPPER_CHEST = { -tracker_selection-part } upper chest?
tracker_selection_menu-CHEST = { -tracker_selection-part } chest?
tracker_selection_menu-WAIST = { -tracker_selection-part } waist?
tracker_selection_menu-HIP = { -tracker_selection-part } hip?
@@ -491,7 +494,7 @@ onboarding-wifi_creds-password =
## Mounting setup
onboarding-reset_tutorial-back = Go Back to Mounting calibration
onboarding-reset_tutorial = Reset tutorial
onboarding-reset_tutorial-description = While you use your trackers they might get out of alignment because of IMU yaw drift, or because you might have moved them physically. You have several ways to fix this.
onboarding-reset_tutorial-explanation = While you use your trackers they might get out of alignment because of IMU yaw drift, or because you might have moved them physically. You have several ways to fix this.
onboarding-reset_tutorial-skip = Skip step
# Cares about multiline
onboarding-reset_tutorial-0 = Tap { $taps } times the highlighted tracker for triggering yaw reset.
@@ -649,11 +652,11 @@ onboarding-choose_mounting = What mounting calibration method to use?
onboarding-choose_mounting-description = Mounting orientation corrects for the placement of trackers on your body.
onboarding-choose_mounting-auto_mounting = Automatic mounting
# Italized text
onboarding-choose_mounting-auto_mounting-subtitle = Experimental
onboarding-choose_mounting-auto_mounting-label = Experimental
onboarding-choose_mounting-auto_mounting-description = This will automatically detect the mounting directions for all of your trackers from 2 poses
onboarding-choose_mounting-manual_mounting = Manual mounting
# Italized text
onboarding-choose_mounting-manual_mounting-subtitle = Recommended
onboarding-choose_mounting-manual_mounting-label = Recommended
onboarding-choose_mounting-manual_mounting-description = This will let you choose the mounting direction manually for each tracker
@@ -697,7 +700,7 @@ onboarding-choose_proportions-manual_proportions = Manual proportions
# Italized text
onboarding-choose_proportions-manual_proportions-subtitle = For small touches
onboarding-choose_proportions-manual_proportions-description = This will let you adjust your proportions manually by modifying them directly
onboarding-choose_proportions-save = Export proportions
onboarding-choose_proportions-export = Export proportions
onboarding-choose_proportions-file_type = Body proportions file
## Tracker manual proportions setup

View File

@@ -532,20 +532,20 @@ onboarding-reset_tutorial-skip = Saltar paso
# Cares about multiline
onboarding-reset_tutorial-0 =
Toca { $taps } veces el tracker resaltado para activar el reinicio horizontal.
Esto va a hacer que tus sensores miren para la misma dirección que tu HMD.
# Cares about multiline
onboarding-reset_tutorial-1 =
Toca { $taps } veces el tracker resaltado para activar el reinicio completo.
Se requiere que estas de forma parada (pose en i). Esto tiene un delay de 3 segundos (configurable) antes de que actualmente suceda.
Esto reinicia completamente la posición y rotación de todos tus sensores, debería de arreglar la mayoría de tus problemas.
# Cares about multiline
onboarding-reset_tutorial-2 =
Toca { $taps } veces el tracker resaltado para activar el reinicio de montura.
El reinicio de montura ayuda en como tus sensores están puestos en tu cuerpo, ya que si los movistes o cambiaste para donde están orientados bastante, esto debería de ayudar.
Requiere que estas en una pose como que estás esquiando, como se muestra en el tutorial de montura automática y tenes un retraso de 3 segundos (configurable) antes de que actualmente suceda.
## Setup start
@@ -758,7 +758,7 @@ onboarding-choose_proportions-manual_proportions = Proporciones manuales
# Italized text
onboarding-choose_proportions-manual_proportions-subtitle = Para toques pequeños
onboarding-choose_proportions-manual_proportions-description = Esto te permitirá ajustar tus proporciones manualmente de forma directa
onboarding-choose_proportions-save = Exportar proporciones
onboarding-choose_proportions-export = Exportar proporciones
onboarding-choose_proportions-file_type = Archivo de proporciones del cuerpo
## Tracker manual proportions setup

View File

@@ -527,7 +527,7 @@ onboarding-wifi_creds-password =
onboarding-reset_tutorial-back = Retourner à l'alignement des capteurs
onboarding-reset_tutorial = Didacticiel de réinitialisation
onboarding-reset_tutorial-description = Cette fonctionnalité n'est pas encore terminée, appuyez simplement sur continuer
onboarding-reset_tutorial-explanation = Pendant que vous utilisez vos capteurs, ils peuvent se désaligner à cause de la dérive horizontale du IMU, ou parce que vous les avez déplacés physiquement. Vous avez plusieurs façons de résoudre ce problème.
onboarding-reset_tutorial-skip = Sauter l'étape
# Cares about multiline
onboarding-reset_tutorial-0 =
@@ -707,11 +707,11 @@ onboarding-choose_mounting = Quelle méthode de calibration de lalignement ut
onboarding-choose_mounting-description = La calibration de l'alignement ajuste pour l'orientation des capteurs sur votre corps.
onboarding-choose_mounting-auto_mounting = Alignement automatique
# Italized text
onboarding-choose_mounting-auto_mounting-subtitle = Expérimentale
onboarding-choose_mounting-auto_mounting-label = Expérimentale
onboarding-choose_mounting-auto_mounting-description = Ceci permettra de détecter automatiquement la direction de tous vos capteurs à partir de 2 poses
onboarding-choose_mounting-manual_mounting = Alignement manuel
# Italized text
onboarding-choose_mounting-manual_mounting-subtitle = Recommendée
onboarding-choose_mounting-manual_mounting-label = Recommendée
onboarding-choose_mounting-manual_mounting-description = Ceci vous permettra de choisir la direction de chaque capteur manuellement
## Tracker manual mounting setup
@@ -758,7 +758,7 @@ onboarding-choose_proportions-manual_proportions = Proportions manuelles
# Italized text
onboarding-choose_proportions-manual_proportions-subtitle = Pour les retouches
onboarding-choose_proportions-manual_proportions-description = Ceci vous permettra d'ajuster vos proportions manuellement en les modifiant directement
onboarding-choose_proportions-save = Exporter les proportions
onboarding-choose_proportions-export = Exporter les proportions
onboarding-choose_proportions-file_type = Fichier de proportions
## Tracker manual proportions setup

View File

@@ -1,6 +1,3 @@
### SlimeVR complete GUI translations
# Please developers (not translators) don't reuse a key inside another key
# or concat text with a translation string in the code, use the appropriate
# features like variables and selectors in each appropriate case!
@@ -13,6 +10,9 @@
websocket-connecting = מתחבר לשרת
websocket-connection_lost = החיבור לשרת אבד. מנסה להתחבר מחדש
## Update notification
## Tips
tips-find_tracker = לא בטוח איזה חיישן אתה מחזיק? נער את החיישן והתוכנה תסמן לך אותו.
@@ -30,7 +30,6 @@ body_part-RIGHT_HAND = יד ימין
body_part-RIGHT_UPPER_LEG = ירך ימין
body_part-RIGHT_LOWER_LEG = קרסול ימין
body_part-RIGHT_FOOT = רגל ימין
body_part-RIGHT_CONTROLLER = בקר ימני
body_part-CHEST = חזה
body_part-WAIST = מותניים
body_part-HIP = ירך
@@ -41,7 +40,6 @@ body_part-LEFT_HAND = יד שמאל
body_part-LEFT_UPPER_LEG = ירך שמאל
body_part-LEFT_LOWER_LEG = קרסול שמאל
body_part-LEFT_FOOT = רגל שמאל
body_part-LEFT_CONTROLLER = בקר שמאלי
## Proportions
@@ -58,15 +56,12 @@ skeleton_bone-SHOULDERS_DISTANCE = מרחק כתפיים
skeleton_bone-SHOULDERS_WIDTH = רוחב כתפיים
skeleton_bone-UPPER_ARM = אורך זרוע עליונה
skeleton_bone-LOWER_ARM = אורך זרוע תחתונה
skeleton_bone-CONTROLLER_Y = מרחק בקר ציר Y
skeleton_bone-CONTROLLER_Z = מרחק בקר ציר Z
## Tracker reset buttons
reset-reset_all = איפוס כל הפרופורציות
reset-full = איפוס
reset-mounting = איפוס הרכבה
reset-quick = איפוס מהיר
## Serial detection stuff
@@ -87,11 +82,14 @@ navbar-mounting = כיול ההרכבה
navbar-onboarding = אשף ההגדרה
navbar-settings = הגדרות
## Bounding volume hierarchy recording
## Biovision hierarchy recording
bvh-start_recording = הקלטת BVH
bvh-recording = מקליט...
## Tracking pause
## Widget: Overlay settings
widget-overlay = ממשק Overlay
@@ -256,6 +254,13 @@ settings-general-tracker_mechanics-drift_compensation-max_resets-label = שימ
## OSC VRChat settings
settings-osc-vrchat-network-trackers-chest = חזה
settings-osc-vrchat-network-trackers-knees = ברכיים
settings-osc-vrchat-network-trackers-feet = רגל
settings-osc-vrchat-network-trackers-elbows = מרפקים
## VMC OSC settings
## Setup/onboarding menu
@@ -278,24 +283,35 @@ settings-general-tracker_mechanics-drift_compensation-max_resets-label = שימ
## Tracker connection setup
## Tracker calibration tutorial
## Tracker assignment tutorial
## Tracker assignment setup
## Tracker assignment warnings
## Tracker mounting method choose
## Tracker manual mounting setup
## Tracker automatic mounting setup
## Tracker proportions method choose
## Tracker manual proportions setup
## Tracker automatic proportions setup
onboarding-automatic_proportions-recording-steps-4 = זוז במקום עד שהספירה תסתיים
onboarding-automatic_proportions-recording-processing = מעבד את התוצאה
onboarding-automatic_proportions-verify_results-title = אמת את התוצאות
onboarding-automatic_proportions-verify_results-description = אנא בדוק את התוצאות, האם התוצאות נראות נכון?
@@ -309,3 +325,6 @@ onboarding-automatic_proportions-done-description = תהליך כיול פרופ
## Home
home-no_trackers = לא זוהו או הוקצו חיישנים
## Status system

View File

@@ -12,6 +12,8 @@ websocket-connection_lost = サーバーへの接続が失われました。再
## Update notification
version_update-title = 新しいバージョンが利用可能です:{ $version }
version_update-description = { version_update-update }をクリックすると、SlimeVRインストーラーがダウンロードされます。
version_update-update = アップデート
version_update-close = 閉じる
@@ -19,6 +21,8 @@ version_update-close = 閉じる
tips-find_tracker = どのトラッカーがどれだかわからない?トラッカーを振ると、該当する項目がハイライトされます。
tips-do_not_move_heels = レコーディング中にかかとが動かないように注意しましょう!
tips-file_select = 使用するファイルをドラッグ&ドロップするか、 <u>参照</u>します。
tips-tap_setup = 追跡装置をゆっくり2回軽くタップして選択することができます、メニューから選ぶ必要はありません
## Body parts
@@ -48,12 +52,14 @@ body_part-LEFT_FOOT = 左足先
skeleton_bone-NONE = 無し
skeleton_bone-HEAD = ヘッドシフト
skeleton_bone-NECK = 首長さ
skeleton_bone-CHEST = 胸囲
skeleton_bone-torso_group = 胴体の長さ
skeleton_bone-CHEST_OFFSET = 胸オフセット
skeleton_bone-CHEST = 胸囲
skeleton_bone-WAIST = ウエスト長さ
skeleton_bone-HIP = ヒップ長さ
skeleton_bone-HIP_OFFSET = ヒップオフセット
skeleton_bone-HIPS_WIDTH = ヒップ幅
skeleton_bone-leg_group = 股下の長さ
skeleton_bone-UPPER_LEG = 膝長さ
skeleton_bone-LOWER_LEG = 足長さ
skeleton_bone-FOOT_LENGTH = 足先長さ
@@ -61,8 +67,11 @@ skeleton_bone-FOOT_SHIFT = 足先シフト
skeleton_bone-SKELETON_OFFSET = スケルトンオフセット
skeleton_bone-SHOULDERS_DISTANCE = 肩の距離
skeleton_bone-SHOULDERS_WIDTH = 肩幅
skeleton_bone-arm_group = 腕の長さ
skeleton_bone-UPPER_ARM = 上腕長さ
skeleton_bone-LOWER_ARM = 前腕長さ
skeleton_bone-HAND_Y = 手の距離 Y
skeleton_bone-HAND_Z = 手の距離Z
skeleton_bone-ELBOW_OFFSET = 肘オフセット
## Tracker reset buttons
@@ -98,6 +107,8 @@ bvh-recording = レコーディング中...
## Tracking pause
tracking-unpaused = トラッキング停止
tracking-paused = トラッキング再開
## Widget: Overlay settings
@@ -107,6 +118,7 @@ widget-overlay-is_mirrored_label = オーバーレイをミラーとして表示
## Widget: Drift compensation
widget-drift_compensation-clear = ドリフト補正をクリアする
## Widget: Developer settings
@@ -161,7 +173,11 @@ tracker-infos-manufacturer = メーカ-
tracker-infos-display_name = 表示名
tracker-infos-custom_name = カスタム名称
tracker-infos-url = トラッカーURL
tracker-infos-version = ファームウェアバージョン
tracker-infos-hardware_rev = ハードウエアのリビジョン
tracker-infos-hardware_identifier = ハードウェアID
tracker-infos-imu = 慣性計測センサー
tracker-infos-board_type = メインボード
## Tracker settings
@@ -228,6 +244,9 @@ tracker_selection_menu-LEFT_CONTROLLER = { -tracker_selection-part(body-part: "
tracker_selection_menu-unassigned = 未割り当てのトラッカー
tracker_selection_menu-assigned = 割り当て済みのトラッカー
tracker_selection_menu-dont_assign = 割り当てない
# This line cares about multilines.
# <b>text</b> means that the text should be bold.
tracker_selection_menu-neck_warning = <b>警告:</b>首のトラッカーを締め付けすぎると、頭部の血液循環に危険が生じる可能性があります!
tracker_selection_menu-neck_warning-done = リスクを理解しています
tracker_selection_menu-neck_warning-cancel = キャンセル
@@ -245,6 +264,7 @@ settings-sidebar-fk_settings = FK設定
settings-sidebar-gesture_control = ジェスチャーコントロール
settings-sidebar-interface = インターフェース
settings-sidebar-osc_router = OSCルーター
settings-sidebar-osc_trackers = VRChatOSCトラッカー
settings-sidebar-utils = ユーティリティ
settings-sidebar-serial = シリアルコンソール
@@ -304,6 +324,7 @@ settings-general-fk_settings-leg_tweak-floor_clip = フロアクリップ
# definition - Guesses when each foot is in contact with the ground and uses that information to improve tracking
settings-general-fk_settings-leg_tweak-skating_correction = スケーティング補正
settings-general-fk_settings-leg_tweak-skating_correction-amount = スケーティング補正の強さ
settings-general-fk_settings-leg_fk = 足のトラッキング
settings-general-fk_settings-arm_fk = アームトラッキング
settings-general-fk_settings-arm_fk-description = 腕の追従方法を変更する。
settings-general-fk_settings-arm_fk-force_arms = Force arms from HMD
@@ -350,6 +371,7 @@ settings-general-interface-serial_detection-label = シリアルデバイスの
settings-general-interface-feedback_sound = フィードバック音
settings-general-interface-feedback_sound-label = フィードバック音
settings-general-interface-feedback_sound-volume = フィードバック音量
settings-general-interface-theme = カラーテーマ
settings-general-interface-lang = 言語を選択
settings-general-interface-lang-description = 使用したいデフォルトの言語を変更する
settings-general-interface-lang-placeholder = 使用する言語を選択する
@@ -419,6 +441,7 @@ settings-osc-vrchat-network-address-placeholder = VRChatのIPアドレス
settings-osc-vrchat-network-trackers = トラッカー
settings-osc-vrchat-network-trackers-description = データの送受信を切り替える。
settings-osc-vrchat-network-trackers-chest = 胸
settings-osc-vrchat-network-trackers-hip = 腰
settings-osc-vrchat-network-trackers-knees = 膝
settings-osc-vrchat-network-trackers-feet = 足
settings-osc-vrchat-network-trackers-elbows = 肘
@@ -429,6 +452,12 @@ settings-osc-vmc = バーチャルモーションキャプチャ
settings-osc-vmc-enable = 有効
settings-osc-vmc-enable-label = 有効
settings-osc-vmc-network = ネットワークポート
settings-osc-vmc-network-port_in =
.label = ポートイン
.placeholder = ポートイン(デフォルト:39540)
settings-osc-vmc-network-port_out =
.label = ポートアウト
.placeholder = ポートアウト(デフォルト:39539)
settings-osc-vmc-network-address = ネットワークアドレス
settings-osc-vmc-network-address-placeholder = IPV4アドレス
settings-osc-vmc-vrm = VRMモデル
@@ -438,6 +467,8 @@ settings-osc-vmc-vrm = VRMモデル
onboarding-skip = 設定をスキップする
onboarding-continue = 続ける
onboarding-wip = 実行中
onboarding-setup_warning-skip = セットアップをスキップする
onboarding-setup_warning-cancel = セットアップを続行する
## Wi-Fi setup
@@ -460,7 +491,7 @@ onboarding-wifi_creds-password =
onboarding-reset_tutorial-back = マウントキャリブレーションに戻る
onboarding-reset_tutorial = リセットチュートリアル
onboarding-reset_tutorial-description = この機能は終了していません。続けるを押してください。
onboarding-reset_tutorial-skip = ステップをスキップする
## Setup start
@@ -488,7 +519,12 @@ onboarding-connect_tracker-description-p0 = さあ、楽しい部分に移りま
onboarding-connect_tracker-description-p1 = まだ接続されていないトラッカーたちをUSBポートを通して接続するだけです。
onboarding-connect_tracker-issue-serial = 接続に問題があります!
onboarding-connect_tracker-usb = USBトラッカー
onboarding-connect_tracker-connection_status-none = トラッカーを探しています
onboarding-connect_tracker-connection_status-connecting = Wi-Fiの認証情報を送信中
onboarding-connect_tracker-connection_status-looking_for_server = サーバーを探しています
onboarding-connect_tracker-connection_status-connection_error = Wi-Fiに接続できません
onboarding-connect_tracker-connection_status-could_not_find_server = サーバーが見つかりません
onboarding-connect_tracker-connection_status-done = サーバーに接続されました
# $amount (Number) - Amount of trackers connected (this is a number, but you can use CLDR plural rules for your language)
# More info on https://www.unicode.org/cldr/cldr-aux/charts/22/supplemental/language_plural_rules.html
# English in this case only has 2 plural rules, which are "one" and "other",
@@ -504,10 +540,14 @@ onboarding-connect_tracker-next = すべてのトラッカーを接続しまし
## Tracker calibration tutorial
onboarding-calibration_tutorial = IMU校正チュートリアル
onboarding-calibration_tutorial-subtitle = これにより、センサーのドリフトを減らすことが役立ちます
onboarding-calibration_tutorial-status-calibrating = 校正中
## Tracker assignment tutorial
# This text has a character limit of around 11 characters, so please keep it short
onboarding-assignment_tutorial-sticker = ステッカー
## Tracker assignment setup
@@ -530,8 +570,6 @@ onboarding-assign_trackers-next = すべてのトラッカーを割り当てま
## Tracker mounting method choose
# Italized text
onboarding-choose_mounting-auto_mounting-subtitle = おすすめされた
## Tracker manual mounting setup
@@ -614,3 +652,10 @@ home-no_trackers = トラッカーを検出できません。もしくは割り
## Status system
status_system-StatusTrackerReset = 一つ以上のトラッカーが調整されていないため、完全なリセットを実行することをお勧めします
status_system-StatusSteamVRDisconnected =
{ $type ->
[steamvr_feeder] SlimeVR Feederアプリに接続されていません
*[other] SlimeVRドライバ経由でSteamVRに接続されていません
}
status_system-StatusTrackerError = { $trackerName } トラッカーにエラーが発生しています

View File

@@ -1,6 +1,3 @@
### SlimeVR complete GUI translations
# Please developers (not translators) don't reuse a key inside another key
# or concat text with a translation string in the code, use the appropriate
# features like variables and selectors in each appropriate case!
@@ -13,11 +10,19 @@
websocket-connecting = 서버에 연결하는 중...
websocket-connection_lost = 서버와의 연결이 끊어졌어요. 다시 연결하는 중...
## Update notification
version_update-title = 새로운 버전 발견: { $version }
version_update-description = "{ version_update-update }"를 눌러 설치 프로그램을 다운로드하세요.
version_update-update = 업데이트
version_update-close = 닫기
## Tips
tips-find_tracker = 내 트래커가 어떤 트래커인지 모르시겠다구요? 트래커를 흔들면 해당 항목이 빛날 거예요.
tips-do_not_move_heels = 기록하는 동안 발뒤꿈치가 움직이지 않도록 조심하세요!
tips-file_select = 파일을 <u>열거나,</u> 여기에 드래그&드롭하세요.
tips-tap_setup = 목록에서 트래커를 선택하는 대신 할당할 트래커를 천천히 2번 탭해서 선택할 수 있어요.
## Body parts
@@ -95,11 +100,16 @@ navbar-mounting = 착용 방향 정렬
navbar-onboarding = 설정 마법사
navbar-settings = 설정
## Bounding volume hierarchy recording
## Biovision hierarchy recording
bvh-start_recording = BVH 기록
bvh-recording = 기록중...
## Tracking pause
tracking-unpaused = 트래킹 일시 중지
tracking-paused = 트래킹 재개
## Widget: Overlay settings
widget-overlay = 오버레이
@@ -126,6 +136,7 @@ widget-developer_mode-more_info = 더 많은 정보 보기
widget-imu_visualizer = 회전
widget-imu_visualizer-rotation_raw = Raw
widget-imu_visualizer-rotation_preview = 미리보기
widget-imu_visualizer-rotation_hide = 숨기기
## Tracker status
@@ -164,6 +175,9 @@ tracker-infos-custom_name = 사용자 정의 이름
tracker-infos-url = 트래커 URL
tracker-infos-version = 펌웨어 버전
tracker-infos-hardware_rev = 하드웨어 리비전
tracker-infos-hardware_identifier = 하드웨어 ID
tracker-infos-imu = IMU 센서
tracker-infos-board_type = 메인보드
## Tracker settings
@@ -252,6 +266,7 @@ settings-sidebar-fk_settings = FK 설정
settings-sidebar-gesture_control = 제스처 제어
settings-sidebar-interface = 인터페이스
settings-sidebar-osc_router = OSC 라우터
settings-sidebar-osc_trackers = VRChat OSC 트래커
settings-sidebar-utils = 유틸리티
settings-sidebar-serial = 시리얼 콘솔
@@ -439,7 +454,7 @@ settings-osc-vrchat-network-address-placeholder = VRChat IP 주소
settings-osc-vrchat-network-trackers = 트래커
settings-osc-vrchat-network-trackers-description = 활성화해서 데이터 송수신
settings-osc-vrchat-network-trackers-chest = Chest
settings-osc-vrchat-network-trackers-waist = Waist
settings-osc-vrchat-network-trackers-hip = 골반
settings-osc-vrchat-network-trackers-knees = Knees
settings-osc-vrchat-network-trackers-feet = Feet
settings-osc-vrchat-network-trackers-elbows = Elbows
@@ -511,7 +526,6 @@ onboarding-wifi_creds-password =
onboarding-reset_tutorial-back = 착용 방향 정렬로 돌아가기
onboarding-reset_tutorial = 정렬 튜토리얼
onboarding-reset_tutorial-description = 이 기능은 아직 완성되지 않았어요, 지금은 일단 계속하기를 눌러주세요!
## Setup start
@@ -559,6 +573,22 @@ onboarding-connect_tracker-connected_trackers =
}
onboarding-connect_tracker-next = 모든 트래커를 잘 연결했어요
## Tracker calibration tutorial
onboarding-calibration_tutorial = IMU 캘리브레이션 튜토리얼
onboarding-calibration_tutorial-subtitle = 트래커 틀어짐을 줄이는 데 도움이 될 거예요!
onboarding-calibration_tutorial-status-calibrating = 캘리브레이팅
## Tracker assignment tutorial
onboarding-assignment_tutorial = 슬라임 트래커를 착용하기 전에 준비하는 방법
onboarding-assignment_tutorial-first_step = 1. 신체 부위가 적힌 스티커를 가지고 있다면 트래커에 붙여보세요
# This text has a character limit of around 11 characters, so please keep it short
onboarding-assignment_tutorial-sticker = 스티커
onboarding-assignment_tutorial-second_step = 2. 스트랩의 벨크로 테이프 쪽을 그림과 같은 방향으로 유지하면서 스트랩을 트래커에 끼우세요:
onboarding-assignment_tutorial-second_step-continuation = 익스텐션 트래커의 벨크로 테이프는 다음과 같은 방향으로 끼워주세요:
onboarding-assignment_tutorial-done = 스트랩과 스티커를 트래커에 잘 부착했어요!
## Tracker assignment setup
onboarding-assign_trackers-back = Wi-Fi 자격 증명으로 돌아가기
@@ -642,13 +672,11 @@ onboarding-assign_trackers-warning-WAIST =
## Tracker mounting method choose
onboarding-choose_mounting = 착용 방향을 정렬하기 위해 어떤 방법을 사용할래요?
# Multiline text
onboarding-choose_mounting-description = 착용 방향 정렬은 트래커가 몸에 착용된 방향을 찾아 수정하도록 도와줘요.
onboarding-choose_mounting-auto_mounting = 자동으로 방향 설정
# Italized text
onboarding-choose_mounting-auto_mounting-subtitle = 권장
onboarding-choose_mounting-auto_mounting-description = 이렇게 하면 두 가지 자세로 모든 트래커의 착용 방향을 자동으로 설정할 수 있어요
onboarding-choose_mounting-manual_mounting = 수동으로 방향 설정
# Italized text
onboarding-choose_mounting-manual_mounting-subtitle = 무엇을 하려는 지 알고 있다면요
onboarding-choose_mounting-manual_mounting-description = 이렇게 하면 각 트래커의 착용 방향을 직접 고를 수 있어요
## Tracker manual mounting setup
@@ -682,7 +710,7 @@ onboarding-automatic_mounting-put_trackers_on-next = 모든 트래커를 착용
## Tracker proportions method choose
onboarding-choose_proportions = 신체 비율을 설정하 위해 어떤 방법을 사용할래요?
onboarding-choose_proportions = 신체 비율을 설정하 위해 어떤 방법을 사용할래요?
onboarding-choose_proportions-auto_proportions = 자동으로 비율 설정
# Italized text
onboarding-choose_proportions-auto_proportions-subtitle = 권장
@@ -752,3 +780,6 @@ onboarding-automatic_proportions-done-description = 신체 비율 보정이 완
## Home
home-no_trackers = 감지되거나 할당된 트래커가 없어요.
## Status system

View File

@@ -1,6 +1,3 @@
### SlimeVR complete GUI translations
# Please developers (not translators) don't reuse a key inside another key
# or concat text with a translation string in the code, use the appropriate
# features like variables and selectors in each appropriate case!
@@ -103,11 +100,16 @@ navbar-mounting = Bevestigings- kalibratie
navbar-onboarding = Setupgids
navbar-settings = Instellingen
## Bounding volume hierarchy recording
## Biovision hierarchy recording
bvh-start_recording = BVH opnemen
bvh-recording = Opname bezig...
## Tracking pause
tracking-unpaused = Pauzeer tracking
tracking-paused = Herneem tracking
## Widget: Overlay settings
widget-overlay = Overlay
@@ -521,7 +523,7 @@ onboarding-wifi_creds-password =
onboarding-reset_tutorial-back = Ga terug naar de bevestigingskalibratie
onboarding-reset_tutorial = Reset tutorial
onboarding-reset_tutorial-description = Deze stap is nog niet afgewerkt, druk gewoon op doorgaan.
onboarding-reset_tutorial-skip = Stap overslaan
## Setup start
@@ -581,6 +583,9 @@ onboarding-calibration_tutorial-status-calibrating = Kalibreren
onboarding-calibration_tutorial-status-success = Aardig!
onboarding-calibration_tutorial-status-error = De tracker werd verplaatst
## Tracker assignment tutorial
## Tracker assignment setup
onboarding-assign_trackers-back = Ga terug naar de instellingen voor WiFi-configuratie
@@ -670,11 +675,11 @@ onboarding-assign_trackers-warning-WAIST =
onboarding-choose_mounting = Welke montagekalibratiemethode moet worden gebruikt?
onboarding-choose_mounting-auto_mounting = Automatische bevestiging
# Italized text
onboarding-choose_mounting-auto_mounting-subtitle = Aanbevolen
onboarding-choose_mounting-auto_mounting-label = Experimenteel
onboarding-choose_mounting-auto_mounting-description = Dit detecteert automatisch de montagerichtingen voor al uw trackers door middel van 2 poses
onboarding-choose_mounting-manual_mounting = Handmatige bevestiging
# Italized text
onboarding-choose_mounting-manual_mounting-subtitle = Als je weet wat je doet
onboarding-choose_mounting-manual_mounting-label = Aanbevolen
onboarding-choose_mounting-manual_mounting-description = Hiermee kunt u de montagerichting handmatig kiezen voor elke tracker
## Tracker manual mounting setup
@@ -779,3 +784,6 @@ onboarding-automatic_proportions-done-description = Je kalibratie voor lichaamsv
## Home
home-no_trackers = Geen trackers gedetecteerd of toegewezen
## Status system

View File

@@ -36,6 +36,7 @@ body_part-RIGHT_HAND = Prawa Dłoń
body_part-RIGHT_UPPER_LEG = Prawe Udo
body_part-RIGHT_LOWER_LEG = Prawe Podudzie
body_part-RIGHT_FOOT = Prawa Stopa
body_part-UPPER_CHEST = Górna część klatki piersiowej
body_part-CHEST = Klatka Piersiowa
body_part-WAIST = Talia
body_part-HIP = Biodra
@@ -53,8 +54,9 @@ skeleton_bone-NONE = Brak
skeleton_bone-HEAD = Przesunięcie Głowy
skeleton_bone-NECK = Długość Szyi
skeleton_bone-torso_group = Długość torsu
skeleton_bone-CHEST = Długość Klatki Piersiowej
skeleton_bone-UPPER_CHEST = Górna długość klatki piersiowej
skeleton_bone-CHEST_OFFSET = Przesunięcie Klatki Piersiowej
skeleton_bone-CHEST = Długość Klatki Piersiowej
skeleton_bone-WAIST = Długość Talii
skeleton_bone-HIP = Długość Bioder
skeleton_bone-HIP_OFFSET = Przesunięcie Bioder
@@ -230,6 +232,7 @@ tracker_selection_menu-RIGHT_UPPER_LEG = { -tracker_selection-part } prawe udo?
tracker_selection_menu-RIGHT_LOWER_LEG = { -tracker_selection-part } prawa kostka?
tracker_selection_menu-RIGHT_FOOT = { -tracker_selection-part } prawa stopa?
tracker_selection_menu-RIGHT_CONTROLLER = { -tracker_selection-part } prawy kontroler?
tracker_selection_menu-UPPER_CHEST = { -tracker_selection-part } górnej części klatki piersiowej?
tracker_selection_menu-CHEST = { -tracker_selection-part } klatka piersiowa?
tracker_selection_menu-WAIST = { -tracker_selection-part } talia?
tracker_selection_menu-HIP = { -tracker_selection-part } biodro?
@@ -529,7 +532,7 @@ onboarding-wifi_creds-password =
onboarding-reset_tutorial-back = Powrót do Konfiguracji Położenia trackerów
onboarding-reset_tutorial = Zresetuj poradnik
onboarding-reset_tutorial-description = Ta funkcja jeszcze nie jest skończona.
onboarding-reset_tutorial-explanation = Podczas korzystania z trackerów mogą się one rozregulować z powodu dryfu odchylenia IMU lub z powodu fizycznego przeniesienia ich. Możesz to naprawić na kilka sposobów.
onboarding-reset_tutorial-skip = Pomiń krok
# Cares about multiline
onboarding-reset_tutorial-0 =
@@ -713,11 +716,11 @@ onboarding-choose_mounting = Jakiej metody kalibracji montażu użyć?
onboarding-choose_mounting-description = Orientacja montażu koryguje umieszczenie trackerów na ciele.
onboarding-choose_mounting-auto_mounting = Automatyczne mocowanie
# Italized text
onboarding-choose_mounting-auto_mounting-subtitle = Zalecana
onboarding-choose_mounting-auto_mounting-label = Eksperymentalny
onboarding-choose_mounting-auto_mounting-description = To automatycznie wykryje kierunki montażu dla wszystkich twoich trackerów z 2 pozycji
onboarding-choose_mounting-manual_mounting = Montaż ręczny
# Italized text
onboarding-choose_mounting-manual_mounting-subtitle = Jeśli wiesz, co robisz
onboarding-choose_mounting-manual_mounting-label = Zalecany
onboarding-choose_mounting-manual_mounting-description = Umożliwi to ręczne wybranie kierunku montażu dla każdego trackera
## Tracker manual mounting setup
@@ -764,7 +767,7 @@ onboarding-choose_proportions-manual_proportions = Ręczne proporcje
# Italized text
onboarding-choose_proportions-manual_proportions-subtitle = Drobne detale
onboarding-choose_proportions-manual_proportions-description = Umożliwi to ręczne dostosowanie proporcji poprzez ich bezpośrednią modyfikację
onboarding-choose_proportions-save = Zachowaj proporcje
onboarding-choose_proportions-export = Eksportuj proporcje
onboarding-choose_proportions-file_type = Proporcje ciała
## Tracker manual proportions setup

View File

@@ -1,6 +1,3 @@
### SlimeVR complete GUI translations
# Please developers (not translators) don't reuse a key inside another key
# or concat text with a translation string in the code, use the appropriate
# features like variables and selectors in each appropriate case!
@@ -13,10 +10,19 @@
websocket-connecting = Conectando ao servidor
websocket-connection_lost = Conexão perdida com o servidor. Reconectando...
## Update notification
version_update-title = Nova versão disponível: { $version }
version_update-description = Ao clicar em "{ version_update-update }" irá baixar o instalador do SlimeVR para você.
version_update-update = Atualizar
version_update-close = Fechar
## Tips
tips-find_tracker = Não tem certeza qual tracker é qual? Balance o tracker e ele destacará o item correspondente.
tips-do_not_move_heels = Tenha certeza de não mexer seus calcanhares durante a gravação!
tips-file_select = Arraste e solte arquivos para usar, ou <u>pesquise</u>.
tips-tap_setup = Pode tocar lentamente 2 vezes no seu tracker para o escolher em vez de o selecionar no menu.
## Body parts
@@ -70,7 +76,6 @@ skeleton_bone-ELBOW_OFFSET = Compensação do Cotovelo
reset-reset_all = Redefinir todas as proporções
reset-full = Reset
reset-mounting = Reset de Posição
reset-quick = Reset Rápido
## Serial detection stuff
@@ -91,11 +96,14 @@ navbar-mounting = Calibragem de Posição
navbar-onboarding = Assistente de Configuração
navbar-settings = Opções
## Bounding volume hierarchy recording
## Biovision hierarchy recording
bvh-start_recording = Gravar BVH
bvh-recording = Gravando...
## Tracking pause
## Widget: Overlay settings
widget-overlay = Overlay
@@ -297,8 +305,6 @@ settings-general-tracker_mechanics-drift_compensation-max_resets-label = Use at
## FK/Tracking settings
settings-general-fk_settings = Opções de Tracker
settings-general-fk_settings-leg_tweak = Ajustes de perna
settings-general-fk_settings-leg_tweak-description = Atravessar o chão pode reduzir ou até eliminar o clipping(atravessar) com o chão porém pode causar problemas quando ajoelhado. Correção de Deslize corrige o ice skating(deslize dos trackers no chão), porém pode diminuir a precisão de certos padrões de movimento.
# Floor clip:
# why the name - came from the idea of noclip in video games, but is the opposite where clipping to the floor is a desired feature
# definition - Prevents the foot trackers from going lower than they where when a reset was performed
@@ -333,12 +339,6 @@ settings-general-gesture_control-taps =
[one] 1 tap
*[other] { $amount } taps
}
settings-general-gesture_control-quickResetEnabled = Ativar toque para reset rápido
settings-general-gesture_control-quickResetDelay = Delay do reset rápido
settings-general-gesture_control-quickResetTaps = Toques para o reset rápido
settings-general-gesture_control-resetEnabled = Ativar toque para reset
settings-general-gesture_control-resetDelay = Delay do reset
settings-general-gesture_control-resetTaps = Toques para o reset
settings-general-gesture_control-mountingResetEnabled = Toques para o reset de posição
settings-general-gesture_control-mountingResetDelay = Delay do reset de posição
settings-general-gesture_control-mountingResetTaps = Toques para o reset de posição
@@ -426,11 +426,13 @@ settings-osc-vrchat-network-address-placeholder = Endereço de ip do VRChat
settings-osc-vrchat-network-trackers = Trackers
settings-osc-vrchat-network-trackers-description = Ligar ou desligar o envio e recepção de dados.
settings-osc-vrchat-network-trackers-chest = Peito
settings-osc-vrchat-network-trackers-waist = Cintura
settings-osc-vrchat-network-trackers-knees = Joelhos
settings-osc-vrchat-network-trackers-feet = Pés
settings-osc-vrchat-network-trackers-elbows = Cotovelos
## VMC OSC settings
## Setup/onboarding menu
onboarding-skip = Pular configurações
@@ -463,10 +465,6 @@ onboarding-reset_tutorial-description = Esse recurso não está concluído, apen
## Setup start
onboarding-home = Bem vindo ao SlimeVR
# This cares about multilines and it's centered!!
onboarding-home-description =
Trazendo full-body tracking
para todos
onboarding-home-start = Vamos configurar!
## Enter VR part of setup
@@ -511,6 +509,12 @@ onboarding-connect_tracker-connected_trackers =
} connected
onboarding-connect_tracker-next = Eu conectei todos os meus trackers
## Tracker calibration tutorial
## Tracker assignment tutorial
## Tracker assignment setup
onboarding-assign_trackers-back = Voltar para as credenciais de Wi-Fi
@@ -595,6 +599,9 @@ onboarding-assign_trackers-warning-WAIST =
*[unknown] Cintura está atribuído, porém a parte do corpo desconhecida não atribuída também precisa ser atribuída!
}
## Tracker mounting method choose
## Tracker manual mounting setup
onboarding-manual_mounting-back = Voltar para entrar no VR
@@ -624,6 +631,9 @@ onboarding-automatic_mounting-put_trackers_on-title = Coloque seus trackers
onboarding-automatic_mounting-put_trackers_on-description = Para calibrar as rotações de posicionamento, usaremos os trackers que você atribuiu. Coloque todos os seus trackers, você pode ver qual é qual na figura na direita.
onboarding-automatic_mounting-put_trackers_on-next = Coloquei todos os meus trackers
## Tracker proportions method choose
## Tracker manual proportions setup
onboarding-manual_proportions-back = Voltar para o tutorial de reset
@@ -641,22 +651,14 @@ onboarding-automatic_proportions-prev_step = Passo anterior
onboarding-automatic_proportions-put_trackers_on-title = Coloque seus trackers
onboarding-automatic_proportions-put_trackers_on-description = Para calibrar suas proporções, usaremos os trackers que você atribuiu. Coloque todos os seus trackers, você pode ver quais são quais na figura à direita.
onboarding-automatic_proportions-put_trackers_on-next = Coloquei todos os meus trackers
onboarding-automatic_proportions-preparation-title = Preparação
onboarding-automatic_proportions-preparation-description = Coloque uma cadeira diretamente atrás de você dentro da sua área de jogo(Play space). Esteja preparado para sentar durante a configuração de autobone.
onboarding-automatic_proportions-preparation-next = Estou em frente a uma cadeira
onboarding-automatic_proportions-start_recording-title = Esteja preparado para se mexer
onboarding-automatic_proportions-start_recording-description = Começaremos a gravar algumas poses e movimentos específicos. Estes serão solicitados na próxima tela. Esteja preparado para começar quando o botão for pressionado!
onboarding-automatic_proportions-start_recording-next = Começar Gravação
onboarding-automatic_proportions-recording-title = GRAVAR
onboarding-automatic_proportions-recording-description-p0 = Gravação em progresso...
onboarding-automatic_proportions-recording-description-p1 = Faça os movimentos apresentados abaixo:
onboarding-automatic_proportions-recording-steps-0 = Dobre os joelhos algumas vezes.
onboarding-automatic_proportions-recording-steps-1 = Sente-se na cadeira e se levante.
onboarding-automatic_proportions-recording-steps-2 = Gire seu tronco para esquerda, e incline para direita.
onboarding-automatic_proportions-recording-steps-3 = Gire seu tronco para direita, e incline para esquerda.
onboarding-automatic_proportions-recording-steps-4 = Mexa-se até o tempo terminar.
onboarding-automatic_proportions-recording-processing = Processando o resultado
# $time (Number) - Seconds left for the automatic calibration recording to finish (max 15)
# $time (Number) - Seconds left for the automatic calibration recording to finish (max 20)
onboarding-automatic_proportions-recording-timer =
{ $time ->
[one] 1 second left
@@ -674,3 +676,6 @@ onboarding-automatic_proportions-done-description = Sua calibragem de proporçã
## Home
home-no_trackers = Nenhum tracker detectado ou atribuído
## Status system

View File

@@ -1,6 +1,3 @@
### SlimeVR complete GUI translations
# Please developers (not translators) don't reuse a key inside another key
# or concat text with a translation string in the code, use the appropriate
# features like variables and selectors in each appropriate case!
@@ -13,11 +10,19 @@
websocket-connecting = Подключение к серверу
websocket-connection_lost = Потеряно соединение с сервером. Переподключение...
## Update notification
version_update-title = Доступна новая версия: { $version }
version_update-description = Нажав «{ version_update-update }», вы загрузите установщик SlimeVR.
version_update-update = Обновить
version_update-close = Закрыть
## Tips
tips-find_tracker = Не уверены, какой это трекер? Встряхните его, и трекер выделится в списке.
tips-do_not_move_heels = Убедитесь, что ваши пятки не двигаются во время записи!
tips-file_select = Выберите или перетащите файлы для использования <u>выбрать</u>.
tips-tap_setup = Вы можете медленно нажать 2 раза на свой трекер, чтобы выбрать его, вместо того чтобы выбирать его из меню.
## Body parts
@@ -95,11 +100,16 @@ navbar-mounting = Калибровка крепления
navbar-onboarding = Мастер настройки
navbar-settings = Настройки
## Bounding volume hierarchy recording
## Biovision hierarchy recording
bvh-start_recording = Запись BVH
bvh-recording = Запись...
## Tracking pause
tracking-unpaused = Приостановить отслеживание
tracking-paused = Возобновить отслеживание
## Widget: Overlay settings
widget-overlay = Оверлей
@@ -126,6 +136,7 @@ widget-developer_mode-more_info = Дополнительная информац
widget-imu_visualizer = Вращение
widget-imu_visualizer-rotation_raw = RAW
widget-imu_visualizer-rotation_preview = Предпросмотр
widget-imu_visualizer-rotation_hide = Скрыть
## Tracker status
@@ -164,6 +175,9 @@ tracker-infos-custom_name = Свое имя
tracker-infos-url = URL трекера
tracker-infos-version = Версия прошивки
tracker-infos-hardware_rev = Ревизия устройства
tracker-infos-hardware_identifier = ID оборудования
tracker-infos-imu = Датчик IMU
tracker-infos-board_type = Основная плата
## Tracker settings
@@ -176,7 +190,7 @@ tracker-settings-mounting_section = Положение крепления
tracker-settings-mounting_section-description = Где прикреплен трекер?
tracker-settings-mounting_section-edit = Изменить прикрепление
tracker-settings-drift_compensation_section = Разрешить компенсацию дрифта
tracker-settings-drift_compensation_section-description = Должен ли этот трекер компенсировать свой дрифт, когда включена компенсация дрифта?
tracker-settings-drift_compensation_section-description = Должен ли этот трекер компенсировать свой дрифт?
tracker-settings-drift_compensation_section-edit = Разрешить компенсацию дрифта
# The .<name> means it's an attribute and it's related to the top key.
# In this case that is the settings for the assignment section.
@@ -252,6 +266,7 @@ settings-sidebar-fk_settings = Настройки трекеров
settings-sidebar-gesture_control = Настройки жестов
settings-sidebar-interface = Интерфейс
settings-sidebar-osc_router = OSC роутер
settings-sidebar-osc_trackers = VRChat OSC Трекеры
settings-sidebar-utils = Утилиты
settings-sidebar-serial = Консоль
@@ -442,6 +457,7 @@ settings-osc-vrchat-network-address-placeholder = VRChat ip адрес
settings-osc-vrchat-network-trackers = Трекеры
settings-osc-vrchat-network-trackers-description = Переключите отправку определенных трекеров через OSC.
settings-osc-vrchat-network-trackers-chest = Грудь
settings-osc-vrchat-network-trackers-hip = Таз
settings-osc-vrchat-network-trackers-knees = Колени
settings-osc-vrchat-network-trackers-feet = Ступни
settings-osc-vrchat-network-trackers-elbows = Локти
@@ -490,6 +506,7 @@ onboarding-setup_warning =
<b>Предупреждение.</b> Для правильного отслеживания требуется первоначальная настройка,
это необходимо, если вы впервые используете SlimeVR.
onboarding-setup_warning-skip = Пропустить настройку
onboarding-setup_warning-cancel = Продолжить настройку
## Wi-Fi setup
@@ -512,7 +529,26 @@ onboarding-wifi_creds-password =
onboarding-reset_tutorial-back = Вернуться к калибровке крепления
onboarding-reset_tutorial = Сбросить туториал
onboarding-reset_tutorial-description = Эта функция не завершена, просто нажмите продолжить
onboarding-reset_tutorial-explanation = Пока вы пользуетесь своими трекерами, они могут не выровняться из-за дрейфа IMU при рыскании или из-за того, что вы, возможно, переместили их физически. У вас есть несколько способов исправить это.
onboarding-reset_tutorial-skip = Пропустить шаг
# Cares about multiline
onboarding-reset_tutorial-0 =
Коснитесь { $taps } раз выделенного трекера, чтобы активировать сброс рыскания.
Это заставит трекеры смотреть в том же направлении, что и ваш HMD.
# Cares about multiline
onboarding-reset_tutorial-1 =
Нажмите { $taps } раз выделенный трекер, чтобы запустить полный сброс.
Вы должны стоять для этого в (i-позе). Существует задержка в 3 секунды (настраиваемая), прежде чем сброс произойдет.
Это полностью сбрасывает положение и вращение всех ваших трекеров. Это должно исправить большинство проблем.
# Cares about multiline
onboarding-reset_tutorial-2 =
Нажмите { $taps } несколько раз на выделенный трекер, чтобы активировать сброс настроек.
Сброс монтажа помогает узнать, как на самом деле на вас надеты трекеры, поэтому, если вы случайно переместили их и сильно изменили их ориентацию, это поможет.
Вы должны быть в позе, как будто вы катаетесь на лыжах, как показано в мастере автоматического монтажа, и у вас есть 3-секундная задержка (настраиваемая) перед тем, как она сработает.
## Setup start
@@ -563,6 +599,27 @@ onboarding-connect_tracker-connected_trackers =
}
onboarding-connect_tracker-next = Я подключил все трекеры!
## Tracker calibration tutorial
onboarding-calibration_tutorial = Учебное пособие по калибровке IMU
onboarding-calibration_tutorial-subtitle = Это поможет уменьшить дрифт трекера!
onboarding-calibration_tutorial-description = Каждый раз, когда вы включаете трекеры, они должны на мгновение отдохнуть на плоской поверхности для калибровки. Давайте сделаем то же самое, нажав кнопку «{ onboarding-calibration_tutorial-calibrate }», <b>не перемещайте их!</b>
onboarding-calibration_tutorial-calibrate = Я положил свои трекеры на стол
onboarding-calibration_tutorial-status-waiting = Ждем вас
onboarding-calibration_tutorial-status-calibrating = Калибровка
onboarding-calibration_tutorial-status-success = Хорошо!
onboarding-calibration_tutorial-status-error = Трекер был перемещен
## Tracker assignment tutorial
onboarding-assignment_tutorial = Как подготовить Slime Трекер перед тем, как надеть его
onboarding-assignment_tutorial-first_step = 1. Наклейте стикер с частью тела (если он у вас есть) на трекер по вашему выбору.
# This text has a character limit of around 11 characters, so please keep it short
onboarding-assignment_tutorial-sticker = Стикер
onboarding-assignment_tutorial-second_step = 2. Прикрепите ремешок к вашему трекеру, придерживая лицевую сторону ремешка со стороны крючка и петли в следующем положении:
onboarding-assignment_tutorial-second_step-continuation = Сторона крючка и петли для удлинителя должна быть в этой ориентации:
onboarding-assignment_tutorial-done = Я наклеил стикеры и ремешки!
## Tracker assignment setup
onboarding-assign_trackers-back = Вернуться к вводу данных Wi-Fi
@@ -651,6 +708,17 @@ onboarding-assign_trackers-warning-WAIST =
## Tracker mounting method choose
onboarding-choose_mounting = Какой метод калибровки монтажа использовать?
# Multiline text
onboarding-choose_mounting-description = Ориентация крепления корректирует размещение трекеров на вашем теле.
onboarding-choose_mounting-auto_mounting = Автоматическая привязка
# Italized text
onboarding-choose_mounting-auto_mounting-label = Экспериментальный
onboarding-choose_mounting-auto_mounting-description = Это автоматически определит направления монтажа для всех ваших трекеров из 2 поз
onboarding-choose_mounting-manual_mounting = Ручная привязка
# Italized text
onboarding-choose_mounting-manual_mounting-label = Рекомендованный
onboarding-choose_mounting-manual_mounting-description = Это позволит вам выбрать направление монтажа вручную для каждого трекера
## Tracker manual mounting setup
@@ -683,6 +751,21 @@ onboarding-automatic_mounting-put_trackers_on-next = Я включил и над
## Tracker proportions method choose
onboarding-choose_proportions = Какой метод калибровки пропорций использовать?
# Multiline string
onboarding-choose_proportions-description =
Пропорции тела используются для определения размеров вашего тела. Они необходимы для расчета местоположения трекеров.
Если пропорции вашего тела не соответствуют сохраненным, точность отслеживания будет хуже, и вы заметите такие вещи, как катание на коньках или скольжение, или ваше тело не совсем соответствует вашему аватару.
onboarding-choose_proportions-auto_proportions = Автоматическая привязка
# Italized text
onboarding-choose_proportions-auto_proportions-subtitle = Рекомендуется
onboarding-choose_proportions-auto_proportions-description = Это позволит угадать ваши пропорции, записав образец ваших движений и передав его через алгоритм.
onboarding-choose_proportions-manual_proportions = Ручные пропорции
# Italized text
onboarding-choose_proportions-manual_proportions-subtitle = Для небольших штрихов
onboarding-choose_proportions-manual_proportions-description = Это позволит вам настроить пропорции вручную, изменив их напрямую.
onboarding-choose_proportions-export = Экспорт пропорций
onboarding-choose_proportions-file_type = Файл пропорций тела
## Tracker manual proportions setup
@@ -702,12 +785,30 @@ onboarding-automatic_proportions-prev_step = Предыдущий шаг
onboarding-automatic_proportions-put_trackers_on-title = Наденьте ваши трекеры
onboarding-automatic_proportions-put_trackers_on-description = Чтобы откалибровать ваши пропорции, мы собираемся использовать трекеры, которые вы только что назначили. Включите все свои трекеры, вы можете увидеть, какие из них какие на рисунке справа.
onboarding-automatic_proportions-put_trackers_on-next = Я надел все свои трекеры
onboarding-automatic_proportions-requirements-title = Требования
# Each line of text is a different list item
onboarding-automatic_proportions-requirements-description =
У вас есть как минимум достаточно трекеров, чтобы отслеживать ваши ноги (обычно 5 трекеров).
У вас есть трекеры и гарнитура.
Вы носите трекеры и гарнитуру.
Ваши трекеры и гарнитура подключены к серверу SlimeVR.
Ваши трекеры и гарнитура правильно работают на сервере SlimeVR.
Ваша гарнитура передает данные о местоположении на сервер SlimeVR (обычно это означает, что SteamVR запущен и подключен к SlimeVR с помощью драйвера SlimeVR SteamVR).
onboarding-automatic_proportions-requirements-next = Я прочитал требования
onboarding-automatic_proportions-start_recording-title = Будьте готовы к движению
onboarding-automatic_proportions-start_recording-description = Теперь мы собираемся записать некоторые конкретные позы и движения. Они будут запрошены на следующем экране. Будьте готовы начать, когда кнопка будет нажата!
onboarding-automatic_proportions-start_recording-next = Начать запись
onboarding-automatic_proportions-recording-title = Запись
onboarding-automatic_proportions-recording-description-p0 = Запись в процессе...
onboarding-automatic_proportions-recording-description-p1 = Сделайте эти движения:
# Each line of text is a different list item
onboarding-automatic_proportions-recording-steps =
Стоя прямо, покрутите головой по кругу.
Наклоните спину вперед и присядьте на корточки. Сидя на корточках, посмотрите налево, затем направо.
Поверните верхнюю часть туловища влево (против часовой стрелки), затем наклонитесь к земле.
Поверните верхнюю часть туловища вправо (по часовой стрелке), затем наклонитесь к земле.
Вращайте бедрами круговыми движениями, как будто вы используете хула-хуп.
Если на запись осталось время, вы можете повторять эти действия до тех пор, пока она не будет завершена.
onboarding-automatic_proportions-recording-processing = Обработка результата...
# $time (Number) - Seconds left for the automatic calibration recording to finish (max 20)
onboarding-automatic_proportions-recording-timer =
@@ -729,3 +830,13 @@ onboarding-automatic_proportions-done-description = Калибровка про
## Home
home-no_trackers = Трекеры не обнаружены и не привязаны
## Status system
status_system-StatusTrackerReset = Рекомендуется выполнить полный сброс, так как один или несколько трекеров не настроены.
status_system-StatusSteamVRDisconnected =
{ $type ->
[steamvr_feeder] В настоящее время не подключен к приложению SlimeVR Feeder.
*[other] В настоящее время не подключен к SteamVR через драйвер SlimeVR.
}
status_system-StatusTrackerError = В трекере { $trackerName } обнаружена ошибка.

View File

@@ -1,6 +1,3 @@
### SlimeVR complete GUI translations
# Please developers (not translators) don't reuse a key inside another key
# or concat text with a translation string in the code, use the appropriate
# features like variables and selectors in each appropriate case!
@@ -11,29 +8,69 @@
## Websocket (server) status
websocket-connecting = กำลังเชื่อมต่อกับเซิร์ฟเวอร์
websocket-connection_lost = ขาดการเชื่อมต่อกับเซิร์ฟเวอร์ กำลังลองเชื่อมใหม่
## Update notification
version_update-title = มีเวอใหม่พร้อมแล้ว: { $version }
version_update-update = อัพเดท
version_update-close = ปิด
## Tips
tips-do_not_move_heels = ให้แน่ใจว่าเท้าไม่ขยับระหว่างการอัด/บันทึก
## Body parts
body_part-HEAD = หัว
body_part-NECK = คอ
body_part-RIGHT_SHOULDER = ใหล่ขวา
body_part-RIGHT_UPPER_ARM = แขนขวาส่วนบน
body_part-RIGHT_LOWER_ARM = แขนขวาส่วนล่าง
body_part-RIGHT_HAND = มือขวา
body_part-RIGHT_UPPER_LEG = น่องขาขวา
body_part-RIGHT_LOWER_LEG = ข้อเท้าขวา
body_part-RIGHT_FOOT = เท้าขวา
body_part-CHEST = หน้าอก
body_part-WAIST = เอว
body_part-HIP = สะโพก
body_part-LEFT_SHOULDER = ใหล่ซ้าย
body_part-LEFT_UPPER_ARM = แขนซ้ายส่วนบน
body_part-LEFT_LOWER_ARM = แขนซ้ายส่วนล่าง
body_part-LEFT_HAND = มือซ้าย
body_part-LEFT_UPPER_LEG = น่องขาซ้าย
body_part-LEFT_LOWER_LEG = ข้อเท้าซ้าย
body_part-LEFT_FOOT = เท้าขวา
## Proportions
skeleton_bone-NONE = ไม่มี
skeleton_bone-NECK = ความยาวขอ
skeleton_bone-UPPER_ARM = ความยาวแขนส่วนบน
skeleton_bone-LOWER_ARM = ความยาวแขนส่วนล่าง
## Tracker reset buttons
reset-reset_all = รีเซ็ตสัดส่วนร้างกายทั้งหมด
reset-full = รีเซ็ตทั้งหมด
## Serial detection stuff
serial_detection-open_wifi = เชื่อมต่อ Wi-Fi
serial_detection-close = ปิด
## Navigation bar
navbar-home = หน้าหลัก
navbar-body_proportions = สัดส่วนร่างกาย
navbar-onboarding = Setup Wizard
navbar-settings = ตั้งค่า
## Bounding volume hierarchy recording
## Biovision hierarchy recording
bvh-recording = กำลังอัดบันทึก
## Tracking pause
## Widget: Overlay settings
@@ -44,45 +81,76 @@ websocket-connecting = กำลังเชื่อมต่อกับเซ
## Widget: Developer settings
widget-developer_mode = โหมดนักพัฒนา
widget-developer_mode-sort_by_name = เรียงด้วยชื่อ
widget-developer_mode-more_info = ข้อมูลเพิ่มเติม
## Widget: IMU Visualizer
widget-imu_visualizer = การหมุน
widget-imu_visualizer-rotation_preview = ดูตัวอย่าง
widget-imu_visualizer-rotation_hide = ซ่อน
## Tracker status
tracker-status-none = ไม่มีสถานะ
tracker-status-busy = กังลังทำงาน
tracker-status-error = ข้อผิดผลาด
tracker-status-disconnected = หลุดการเชื่อมต่อ
tracker-status-ok = OK
## Tracker status columns
tracker-table-column-name = ชื่อ
tracker-table-column-type = พิมพ์
tracker-table-column-battery = แบตเตอรี่
tracker-table-column-ping = ความหน่วง
tracker-table-column-temperature = อุณหภูมิ °C
tracker-table-column-url = URL
## Tracker rotation
tracker-rotation-front = หน้า
tracker-rotation-left = ซ้าย
tracker-rotation-right = ขวา
tracker-rotation-back = หลัง
## Tracker information
tracker-infos-manufacturer = ผู้ผลิต
tracker-infos-version = เวอร์ชั่นของเฟิร์มแวร์
tracker-infos-board_type = เมนบอร์ด
## Tracker settings
tracker-settings-assignment_section = กำหนด
## Tracker part card info
tracker-part_card-no_name = ไม่มีชื่อ
## Body assignment menu
## Tracker assignment menu
tracker_selection_menu-neck_warning-done = ฉันเข้าใจในความเสี่ยง
tracker_selection_menu-neck_warning-cancel = ยกเลิก
## Mounting menu
mounting_selection_menu-close = ปิด
## Sidebar settings
settings-sidebar-title = การตั้งค่า
## SteamVR settings
## Tracker mechanics
settings-general-tracker_mechanics-filtering-amount = จำนวน
## FK/Tracking settings
@@ -92,42 +160,85 @@ websocket-connecting = กำลังเชื่อมต่อกับเซ
## Interface settings
settings-general-interface-dev_mode = โหมดนักพัฒนา
settings-general-interface-dev_mode-label = โหมดนักพัฒนา
settings-general-interface-theme = สีธีม
settings-general-interface-lang = เลือกภาษา
settings-general-interface-lang-description = เลือกภาษาตั้งต้นที่คุณต้องการใช้
settings-general-interface-lang-placeholder = เลือกภาษาที่ใช้
## Serial settings
settings-serial-reboot = เริ่มการทำงานใหม่
settings-serial-factory_reset = รีเซ็ตเป็นค่าจากโรงงาน
settings-serial-factory_reset-warning-ok = ฉันรู้ว่าฉันกําลังทําอะไรอยู่
settings-serial-factory_reset-warning-cancel = ยกเลิก
settings-serial-auto_dropdown_item = อัตโนมัติ
## OSC router settings
settings-osc-router-enable = เปิดใช้งาน
settings-osc-router-enable-label = เปิดใช้งาน
settings-osc-router-network = พอร์ตเครือข่าย
## OSC VRChat settings
settings-osc-vrchat-enable = เปิดใช้งาน
settings-osc-vrchat-enable-label = เปิดใช้งาน
settings-osc-vrchat-network-trackers-chest = หน้าอก
settings-osc-vrchat-network-trackers-hip = สะโพก
settings-osc-vrchat-network-trackers-knees = หัวเข่า
settings-osc-vrchat-network-trackers-feet = เท้า
settings-osc-vrchat-network-trackers-elbows = ข้อศอก
## VMC OSC settings
settings-osc-vmc-enable = เปิดใช้งาน
settings-osc-vmc-enable-label = เปิดใช้งาน
## Setup/onboarding menu
onboarding-skip = ข้ามการตั้งค่า
onboarding-continue = ดำเนินการต่อ
onboarding-previous_step = ขั้นตอนก่อนหน้า
onboarding-setup_warning-skip = ข้ามการตั้งค่า
## Wi-Fi setup
onboarding-wifi_creds-submit = ส่ง
onboarding-wifi_creds-ssid =
.label = ชื่อ Wi-Fi
.placeholder = ใส่ชื่อ Wi-Fi
onboarding-wifi_creds-password =
.label = รหัสผ่าน
.placeholder = ใส่รหัสผ่าน
## Mounting setup
## Setup start
onboarding-home = ยินดีต้อนรับสู่ SlimeVR
## Enter VR part of setup
onboarding-enter_vr-ready = ฉันพร้อมแล้ว
## Setup done
onboarding-done-title = คุณพร้อมแล้ว!
## Tracker connection setup
onboarding-connect_tracker-connection_status-done = เชื่อมต่อกับเซิร์ฟเวอร์แล้ว
## Tracker calibration tutorial
onboarding-calibration_tutorial-calibrate = ฉันได้วาง tracker บนโต๊ะแล้ว
onboarding-calibration_tutorial-status-success = เยี่ยม!
## Tracker assignment tutorial
## Tracker assignment setup
@@ -140,18 +251,39 @@ websocket-connecting = กำลังเชื่อมต่อกับเซ
## Tracker manual mounting setup
onboarding-manual_mounting-next = ขั้นตอนถัดไป
## Tracker automatic mounting setup
onboarding-automatic_mounting-next = ขั้นตอนถัดไป
onboarding-automatic_mounting-prev_step = ขั้นตอนก่อนหน้า
onboarding-automatic_mounting-done-restart = ลองอีกครั้ง
onboarding-automatic_mounting-preparation-title = จัดเตรียมพร้อม
## Tracker proportions method choose
# Italized text
onboarding-choose_proportions-auto_proportions-subtitle = แนะนำ
## Tracker manual proportions setup
onboarding-manual_proportions-precision = ปรับด้วยความแม่นยํา
## Tracker automatic proportions setup
onboarding-automatic_proportions-title = วัดสัดส่วนร่างกายของคุณ
onboarding-automatic_proportions-requirements-title = ต้องการ
onboarding-automatic_proportions-requirements-next = ฉันได้อ่านสิ่งที่ต้องการแล้ว
onboarding-automatic_proportions-start_recording-next = เริ่มการบันทึก
onboarding-automatic_proportions-recording-title = REC
onboarding-automatic_proportions-recording-description-p0 = กําลังบันทึก...
onboarding-automatic_proportions-recording-description-p1 = ทําการเคลื่อนไหวที่แสดงด้านล่าง:
onboarding-automatic_proportions-recording-processing = กำลังประมวลผลผลลัพธ์
onboarding-automatic_proportions-verify_results-title = ยืนยันผลลัพธ์
onboarding-automatic_proportions-verify_results-description = ตรวจสอบผลลัพธ์ด้านล่างว่าดูถูกต้องหรือไม่?
## Home
## Status system

View File

@@ -1,6 +1,3 @@
### SlimeVR complete GUI translations
# Please developers (not translators) don't reuse a key inside another key
# or concat text with a translation string in the code, use the appropriate
# features like variables and selectors in each appropriate case!
@@ -13,6 +10,12 @@
websocket-connecting = Sunucuya bağlanılıyor
websocket-connection_lost = Sunucuyla bağlantı kesildi. Tekrar bağlanılmaya çalışılıyor...
## Update notification
version_update-title = Yeni sürüm mevcut: { $version }
version_update-update = Güncelle
version_update-close = Kapat
## Tips
tips-find_tracker = Hangi takipçi hangisi emin değil misin? Takipçilerden birini hareket ettirerek belirleyebilirsin.
@@ -30,7 +33,7 @@ body_part-RIGHT_HAND = Sağ el
body_part-RIGHT_UPPER_LEG = Sağ uyluk
body_part-RIGHT_LOWER_LEG = Sağ ayak bileği
body_part-RIGHT_FOOT = Sağ ayak
body_part-RIGHT_CONTROLLER = Sağ oyun kolu
body_part-UPPER_CHEST = Üst göğüs
body_part-CHEST = Göğüs
body_part-WAIST = Bel
body_part-HIP = Kalça
@@ -41,19 +44,21 @@ body_part-LEFT_HAND = Sol el
body_part-LEFT_UPPER_LEG = Sol uyluk
body_part-LEFT_LOWER_LEG = Sol ayak bileği
body_part-LEFT_FOOT = Sol ayak
body_part-LEFT_CONTROLLER = Sol oyun kolu
## Proportions
skeleton_bone-NONE = Yok
skeleton_bone-HEAD = Kafa hizası
skeleton_bone-NECK = Boyun Uzunluğu
skeleton_bone-CHEST = Göğüs Uzunluğu
skeleton_bone-torso_group = Gövde uzunluğu
skeleton_bone-UPPER_CHEST = Üst Göğüs Uzunluğu
skeleton_bone-CHEST_OFFSET = Göğüs hizası
skeleton_bone-CHEST = Göğüs Uzunluğu
skeleton_bone-WAIST = Bel Uzunluğu
skeleton_bone-HIP = Kalça Uzunluğu
skeleton_bone-HIP_OFFSET = Kalça hizası
skeleton_bone-HIPS_WIDTH = Kalça Genişliği
skeleton_bone-leg_group = Bacak uzunluğu
skeleton_bone-UPPER_LEG = Üst Bacak Uzunluğu
skeleton_bone-LOWER_LEG = Alt Bacak Uzunluğu
skeleton_bone-FOOT_LENGTH = Ayak Uzunluğu
@@ -61,32 +66,41 @@ skeleton_bone-FOOT_SHIFT = Ayak hizası
skeleton_bone-SKELETON_OFFSET = İskelet hizası
skeleton_bone-SHOULDERS_DISTANCE = Omuz Mesafesi
skeleton_bone-SHOULDERS_WIDTH = Omuz Genişliği
skeleton_bone-arm_group = Kol uzunluğu
skeleton_bone-UPPER_ARM = Üst Kol Uzunluğu
skeleton_bone-LOWER_ARM = Alt Kol Uzunluğu
skeleton_bone-CONTROLLER_Y = Oyun kolu uzaklığı Y
skeleton_bone-CONTROLLER_Z = Oyun kolu uzaklığı Z
skeleton_bone-ELBOW_OFFSET = Dirsek hizası
## Tracker reset buttons
reset-reset_all = Tüm oranları sıfırla
reset-full = Sıfırlama
reset-quick = Hızlı Sıfırlama
## Serial detection stuff
serial_detection-new_device-p0 = Yeni seri cihaz algılandı!
serial_detection-new_device-p1 = Wi-Fi bilgilerinizi girin!
serial_detection-new_device-p2 = Lütfen onunla ne yapmak istediğinizi seçin
serial_detection-open_wifi = Wi-Fi'ye bağlan
serial_detection-open_serial = Seri Konsolu Aç
serial_detection-submit = Gönder!
serial_detection-close = Kapat
## Navigation bar
navbar-home = Ana Menü
navbar-body_proportions = Vücut Oranları
navbar-onboarding = Kurulum Sihirbazı
navbar-settings = Ayarlar
## Bounding volume hierarchy recording
## Biovision hierarchy recording
bvh-start_recording = BVH Kaydet
bvh-recording = Kaydediliyor
## Tracking pause
tracking-unpaused = Takibi duraklat
## Widget: Overlay settings
@@ -96,15 +110,36 @@ navbar-settings = Ayarlar
## Widget: Developer settings
widget-developer_mode = Geliştirici Modu
widget-developer_mode-high_contrast = Yüksek kontrast
widget-developer_mode-precise_rotation = Hassas dönüş
widget-developer_mode-sort_by_name = Ada göre sırala
widget-developer_mode-more_info = Daha fazla bilgi
## Widget: IMU Visualizer
widget-imu_visualizer = Rotasyon
widget-imu_visualizer-rotation_preview = Önizle
widget-imu_visualizer-rotation_hide = Gizle
## Tracker status
tracker-status-none = Durum Yok
tracker-status-busy = Meşgul
tracker-status-error = Hata
tracker-status-disconnected = Bağlantı kesildi
tracker-status-ok = İYİ
## Tracker status columns
tracker-table-column-name = İsim
tracker-table-column-type = Tür
tracker-table-column-battery = Pil
tracker-table-column-tps = TPS
tracker-table-column-temperature = Sıcaklık °C
tracker-table-column-rotation = Rotasyon X/Y/Z
tracker-table-column-position = Pozisyon X/Y/Z
tracker-table-column-url = URL
## Tracker rotation
@@ -118,34 +153,95 @@ tracker-rotation-back = Arka
tracker-infos-manufacturer = Üretici
tracker-infos-display_name = Görünen Ad
tracker-infos-custom_name = Özel Ad
tracker-infos-url = Takipçi URL'si
tracker-infos-version = Yazılım Sürümü
tracker-infos-hardware_rev = Donanım Revizyonu
tracker-infos-hardware_identifier = Donanım Kimliği
tracker-infos-imu = IMU Sensör
tracker-infos-board_type = Ana kart
## Tracker settings
tracker-settings-back = Takipçi listesine geri dön
tracker-settings-title = Takipçi ayarları
tracker-settings-assignment_section-description = Tracker'in vücudun hangi kısmına atandığı.
# The .<name> means it's an attribute and it's related to the top key.
# In this case that is the settings for the assignment section.
tracker-settings-name_section = Takipçi adı
tracker-settings-name_section-placeholder = NightyBeast'in sol bacağı
## Tracker part card info
tracker-part_card-no_name = İsimsiz
tracker-part_card-unassigned = Atanmamış
## Body assignment menu
body_assignment_menu = Bu takipçinin nerede olmasını istiyorsunuz?
body_assignment_menu-manage_trackers = Tüm takipçileri yönet
## Tracker assignment menu
# A -translation_key (with a dash in the front) means that it's a label.
# It can only be used in the translation file, it's nice for reusing names and that kind of stuff.
#
# We are using it here because english doesn't require changing the text in each case but
# maybe your language does.
-tracker_selection-part = hangi takipçiyi atayacaksınız?
tracker_selection_menu-HEAD = Başınıza { -tracker_selection-part }
tracker_selection_menu-RIGHT_SHOULDER = Sağ omuzunuza { -tracker_selection-part }
tracker_selection_menu-RIGHT_UPPER_ARM = Sağ üst kolunuza { -tracker_selection-part }
tracker_selection_menu-RIGHT_LOWER_ARM = Sağ alt kolunuza { -tracker_selection-part }
tracker_selection_menu-RIGHT_HAND = Sağ elinize { -tracker_selection-part }
tracker_selection_menu-RIGHT_UPPER_LEG = Say kalçanıza { -tracker_selection-part }
tracker_selection_menu-RIGHT_LOWER_LEG = Sağ ayak bileğinize { -tracker_selection-part }
tracker_selection_menu-RIGHT_FOOT = Sağ ayağınıza { -tracker_selection-part }
tracker_selection_menu-UPPER_CHEST = Üst göğüsünüze { -tracker_selection-part }
tracker_selection_menu-unassigned = Atanmamış takipçiler
tracker_selection_menu-assigned = Atanan takipçiler
tracker_selection_menu-neck_warning-done = Riskleri anlıyorum
tracker_selection_menu-neck_warning-cancel = İptal
## Mounting menu
mounting_selection_menu = Bu takipçinin nerede olmasını istiyorsunuz?
mounting_selection_menu-close = Kapat
## Sidebar settings
settings-sidebar-title = Ayarlar
settings-sidebar-general = Genel
settings-sidebar-interface = Arayüz
settings-sidebar-osc_router = OSC yönlendirici
settings-sidebar-osc_trackers = VRChat OSC Takipçileri
settings-sidebar-serial = Seri konsol
## SteamVR settings
settings-general-steamvr = SteamVR
settings-general-steamvr-subtitle = SteamVR takipçileri
settings-general-steamvr-trackers-waist = Bel
settings-general-steamvr-trackers-chest = Göğüs
settings-general-steamvr-trackers-feet = Ayaklar
settings-general-steamvr-trackers-knees = Dizler
settings-general-steamvr-trackers-elbows = Dirsekler
settings-general-steamvr-trackers-hands = Eller
## Tracker mechanics
settings-general-tracker_mechanics-filtering-amount = Miktar
## FK/Tracking settings
settings-general-fk_settings-leg_fk = Bacak takibi
settings-general-fk_settings-arm_fk = Kol takibi
settings-general-fk_settings-skeleton_settings = İskelet ayarları
settings-general-fk_settings-skeleton_settings-description = İskelet ayarlarınıın veya kapatın. Bunlarıık bırakmanız önerilir.
settings-general-fk_settings-skeleton_settings-extended_spine = Uzatılmış omurga
settings-general-fk_settings-skeleton_settings-extended_pelvis = Uzatılmış pelvis
settings-general-fk_settings-skeleton_settings-extended_knees = Uzatılmış diz
settings-general-fk_settings-vive_emulation-title = Vive emülasyonu
settings-general-fk_settings-vive_emulation-label = Vive emülasyonunu etkinleştir
## Gesture control settings (tracker tapping)
@@ -172,6 +268,9 @@ settings-osc-vrchat-enable = Etkinleştir
settings-osc-vrchat-enable-label = Etkinleştir
settings-osc-vrchat-network-address = Ağ adresi
## VMC OSC settings
## Setup/onboarding menu
@@ -193,15 +292,30 @@ settings-osc-vrchat-network-address = Ağ adresi
## Tracker connection setup
## Tracker calibration tutorial
## Tracker assignment tutorial
## Tracker assignment setup
## Tracker assignment warnings
## Tracker mounting method choose
## Tracker manual mounting setup
## Tracker automatic mounting setup
## Tracker proportions method choose
## Tracker manual proportions setup
@@ -210,3 +324,6 @@ settings-osc-vrchat-network-address = Ağ adresi
## Home
## Status system

View File

@@ -0,0 +1,718 @@
# Please developers (not translators) don't reuse a key inside another key
# or concat text with a translation string in the code, use the appropriate
# features like variables and selectors in each appropriate case!
# And also comment the string if it's something not easy to translate, so you help
# translators on what it means
## Websocket (server) status
websocket-connecting = Підключення до сервера
websocket-connection_lost = З'єднання з сервером втрачено. Повторне підключення...
## Update notification
version_update-title = Доступна нова версія: { $version }
version_update-description = Натискання "{ version_update-update }" почнеться завантаження SlimeVR installer.
version_update-update = Оновлення
version_update-close = Закрити
## Tips
tips-find_tracker = Не знаєте який трекер вибирати? Потрясіть трекер і він підсвітить відповідний пункт.
tips-do_not_move_heels = Переконайтеся, що не рухаєте п'ятами під час запису!
tips-file_select = Перетягніть файли для використання або <u>знайдіть</u>.
tips-tap_setup = Ви можете повільно постукати 2 рази по трекеру, щоб вибрати його, замість того, щоб вибирати його з меню.
## Body parts
body_part-NONE = Не призначено
body_part-HEAD = Голова
body_part-NECK = Шия
body_part-RIGHT_SHOULDER = Праве плече
body_part-RIGHT_UPPER_ARM = Права верхня частина руки
body_part-RIGHT_LOWER_ARM = Права нижня частина руки
body_part-RIGHT_HAND = Права рука
body_part-RIGHT_UPPER_LEG = Праве стегно
body_part-RIGHT_LOWER_LEG = Права щиколотка
body_part-RIGHT_FOOT = Права нога
body_part-UPPER_CHEST = Верхня частина грудей
body_part-CHEST = Груди
body_part-WAIST = Талія
body_part-HIP = Стегно
body_part-LEFT_SHOULDER = Ліве плече
body_part-LEFT_UPPER_ARM = Ліва верхня частина руки
body_part-LEFT_LOWER_ARM = Ліва нижня частина руки
body_part-LEFT_HAND = Ліва рука
body_part-LEFT_UPPER_LEG = Ліве стегно
body_part-LEFT_LOWER_LEG = Ліва щиколотка
body_part-LEFT_FOOT = Ліва нога
## Proportions
skeleton_bone-NONE = Нічого
skeleton_bone-HEAD = Зсув голови
skeleton_bone-NECK = Довжина шиї
skeleton_bone-torso_group = Довжина тулуба
skeleton_bone-UPPER_CHEST = Довжина верхньої частини грудей
skeleton_bone-CHEST_OFFSET = Зміщення грудної клітини
skeleton_bone-CHEST = Довжина грудей
skeleton_bone-WAIST = Довжина талії
skeleton_bone-HIP = Довжина стегна
skeleton_bone-HIP_OFFSET = Зміщення стегна
skeleton_bone-HIPS_WIDTH = Ширина стегон
skeleton_bone-leg_group = Довжина ніг
skeleton_bone-UPPER_LEG = Довжина верхньої частини ноги
skeleton_bone-LOWER_LEG = Довжина гомілки
skeleton_bone-FOOT_LENGTH = Довжина стопи
skeleton_bone-FOOT_SHIFT = Зміщення стопи
skeleton_bone-SKELETON_OFFSET = Зміщення скелета
skeleton_bone-SHOULDERS_DISTANCE = Відстань між плечима
skeleton_bone-SHOULDERS_WIDTH = Ширина плечей
skeleton_bone-arm_group = Довжина руки
skeleton_bone-UPPER_ARM = Довжина верхньої частини руки
skeleton_bone-LOWER_ARM = Довжина нижньої частини руки
skeleton_bone-HAND_Y = Відстань рук Y
skeleton_bone-HAND_Z = Відстань руки Z
skeleton_bone-ELBOW_OFFSET = Зміщення ліктя
## Tracker reset buttons
reset-reset_all = Скинути всі пропорції
reset-full = Повне скидання
reset-mounting = Скинути закріплення
reset-yaw = Скинути нахил
## Serial detection stuff
serial_detection-new_device-p0 = Виявлено новий послідовний пристрій!
serial_detection-new_device-p1 = Введіть дані вашого Wi-Fi!
serial_detection-new_device-p2 = Будь ласка, виберіть, що ви хочете з ним зробити
serial_detection-open_wifi = Підключити до Wi-Fi
serial_detection-open_serial = Відкрити послідовну консоль
serial_detection-submit = Підтвердити!
serial_detection-close = Закрити
## Navigation bar
navbar-home = Домашня сторінка
navbar-body_proportions = Пропорції тіла
navbar-trackers_assign = Призначення трекера
navbar-mounting = Калібрування закріплення
navbar-onboarding = Майстер налаштування
navbar-settings = Параметри
## Biovision hierarchy recording
bvh-start_recording = Запис BVH
bvh-recording = Запис...
## Tracking pause
tracking-unpaused = Призупинити трекінг
tracking-paused = Продовжити трекінг
## Widget: Overlay settings
widget-overlay = Накладання
widget-overlay-is_visible_label = Показати накладання у SteamVR
widget-overlay-is_mirrored_label = Відображення накладання як дзеркала
## Widget: Drift compensation
widget-drift_compensation-clear = Очистити компенсацію дрейфу
## Widget: Developer settings
widget-developer_mode = Режим розробника
widget-developer_mode-high_contrast = Висока контрастність
widget-developer_mode-precise_rotation = Точне обертання
widget-developer_mode-fast_data_feed = Швидка подача даних
widget-developer_mode-filter_slimes_and_hmd = Фільтрація слаймів і шолому
widget-developer_mode-sort_by_name = Сортування за назвою
widget-developer_mode-raw_slime_rotation = Необроблене обертання
widget-developer_mode-more_info = Детальніше
## Widget: IMU Visualizer
widget-imu_visualizer = Обертання
widget-imu_visualizer-rotation_raw = Необроблене
widget-imu_visualizer-rotation_preview = Попередній перегляд
widget-imu_visualizer-rotation_hide = Приховати
## Tracker status
tracker-status-none = Немає статусу
tracker-status-busy = Зайнятий
tracker-status-error = Помилка
tracker-status-disconnected = Відключено
tracker-status-occluded = Закрито
tracker-status-ok = OK
## Tracker status columns
tracker-table-column-name = Ім'я
tracker-table-column-type = Тип
tracker-table-column-battery = Батарея
tracker-table-column-ping = Пінг
tracker-table-column-tps = TPS
tracker-table-column-temperature = Темп. °C
tracker-table-column-linear-acceleration = Прискорення X/Y/Z
tracker-table-column-rotation = Обертання X/Y/Z
tracker-table-column-position = Позиція X/Y/Z
tracker-table-column-url = URL
## Tracker rotation
tracker-rotation-front = Спереду
tracker-rotation-left = Зліва
tracker-rotation-right = Справа
tracker-rotation-back = Ззаду
## Tracker information
tracker-infos-manufacturer = Виробник
tracker-infos-display_name = Відображуване ім'я
tracker-infos-custom_name = Персональне ім'я
tracker-infos-url = URL трекера
tracker-infos-version = Версія прошивки
tracker-infos-hardware_rev = Ревізія обладнання
tracker-infos-hardware_identifier = Ідентифікатор обладнання
tracker-infos-imu = IMU Сенсор
tracker-infos-board_type = Основна плата
## Tracker settings
tracker-settings-back = Повернутися до списку трекерів
tracker-settings-title = Налаштування трекеру
tracker-settings-assignment_section = Призначення
tracker-settings-assignment_section-description = До якої частини тіла призначенний трекер.
tracker-settings-assignment_section-edit = Редагування призначення
tracker-settings-mounting_section = Позиція закріпу
tracker-settings-mounting_section-description = Де закріплено трекер?
tracker-settings-mounting_section-edit = Редагувати закріплення
tracker-settings-drift_compensation_section = Дозволити компенсацію дрейфу
tracker-settings-drift_compensation_section-description = Чи повинен цей трекер компенсувати свій дрейф, коли включена компенсація дрейфу?
tracker-settings-drift_compensation_section-edit = Дозволити компенсацію дрейфу
# The .<name> means it's an attribute and it's related to the top key.
# In this case that is the settings for the assignment section.
tracker-settings-name_section = Ім'я трекера
tracker-settings-name_section-description = Дайте йому миле прізвисько °^°
tracker-settings-name_section-placeholder = Ліва нога NightyBeast
## Tracker part card info
tracker-part_card-no_name = Немає імені
tracker-part_card-unassigned = Непризначений
## Body assignment menu
body_assignment_menu = Де ви хочете, щоб був цей трекер?
body_assignment_menu-description = Виберіть місце, куди потрібно призначити цей трекер. Крім того, ви можете керувати всіма трекерами одночасно, а не по одному.
body_assignment_menu-show_advanced_locations = Відображення розширених точок розташувань
body_assignment_menu-manage_trackers = Керування всіма трекерами
body_assignment_menu-unassign_tracker = Відв'язати трекер
## Tracker assignment menu
# A -translation_key (with a dash in the front) means that it's a label.
# It can only be used in the translation file, it's nice for reusing names and that kind of stuff.
#
# We are using it here because english doesn't require changing the text in each case but
# maybe your language does.
-tracker_selection-part = Який трекер призначити к
tracker_selection_menu-NONE = Який трекер ви хочете відв'язати?
tracker_selection_menu-HEAD = { -tracker_selection-part } голові?
tracker_selection_menu-NECK = { -tracker_selection-part } шиї?
tracker_selection_menu-RIGHT_SHOULDER = { -tracker_selection-part } правому плечу?
tracker_selection_menu-RIGHT_UPPER_ARM = { -tracker_selection-part } правій верхній частині руці?
tracker_selection_menu-RIGHT_LOWER_ARM = { -tracker_selection-part } правій нижній частині руці?
tracker_selection_menu-RIGHT_HAND = { -tracker_selection-part } правій руці?
tracker_selection_menu-RIGHT_UPPER_LEG = { -tracker_selection-part } правому стегну?
tracker_selection_menu-RIGHT_LOWER_LEG = { -tracker_selection-part } правій щиколотці?
tracker_selection_menu-RIGHT_FOOT = { -tracker_selection-part } правій ступні?
tracker_selection_menu-RIGHT_CONTROLLER = { -tracker_selection-part } правому контролеру?
tracker_selection_menu-UPPER_CHEST = { -tracker_selection-part } верхня частина грудей?
tracker_selection_menu-CHEST = { -tracker_selection-part } грудям?
tracker_selection_menu-WAIST = { -tracker_selection-part } талії?
tracker_selection_menu-HIP = { -tracker_selection-part } стегну?
tracker_selection_menu-LEFT_SHOULDER = { -tracker_selection-part } Лівому плечу?
tracker_selection_menu-LEFT_UPPER_ARM = { -tracker_selection-part } лівій верхній частині руки?
tracker_selection_menu-LEFT_LOWER_ARM = { -tracker_selection-part } лівій нижній частині руці?
tracker_selection_menu-LEFT_HAND = { -tracker_selection-part } лівій руці?
tracker_selection_menu-LEFT_UPPER_LEG = { -tracker_selection-part } лівому стегну?
tracker_selection_menu-LEFT_LOWER_LEG = { -tracker_selection-part } лівій щиколотці
tracker_selection_menu-LEFT_FOOT = { -tracker_selection-part } лівій ступні?
tracker_selection_menu-LEFT_CONTROLLER = { -tracker_selection-part } лівому контролеру?
tracker_selection_menu-unassigned = Непризначені трекери
tracker_selection_menu-assigned = Призначені трекери
tracker_selection_menu-dont_assign = Відв'язати
# This line cares about multilines.
# <b>text</b> means that the text should be bold.
tracker_selection_menu-neck_warning =
<b>Попередження:</b> Трекер шиї може бути смертельно небезпечним, якщо його регулювати занадто щільно,
Ремінь може скоротити кровообіг до вашої голови!
tracker_selection_menu-neck_warning-done = Я розумію ризики
tracker_selection_menu-neck_warning-cancel = Скасувати
## Mounting menu
mounting_selection_menu = Де ви хочете, щоб був цей трекер?
mounting_selection_menu-close = Закрити
## Sidebar settings
settings-sidebar-title = Параметри
settings-sidebar-general = Загальні
settings-sidebar-tracker_mechanics = Механіки трекера
settings-sidebar-fk_settings = Налаштування відстеження
settings-sidebar-gesture_control = Управління жестами
settings-sidebar-interface = Інтерфейс
settings-sidebar-osc_router = OSC роутер
settings-sidebar-osc_trackers = VRChat OSC трекери
settings-sidebar-utils = Утиліти
settings-sidebar-serial = Послідовна консоль
## SteamVR settings
settings-general-steamvr = SteamVR
settings-general-steamvr-subtitle = SteamVR трекери
# Not all translation keys support multiline, only the ones that specify it will actually
# split it in lines (that also means you can split in lines however you want in those).
# The first spaces (not tabs) for indentation will be ignored, just to make the file look nice when writing.
# This one is one of this cases that cares about multilines
settings-general-steamvr-description =
Увімкніть або вимкніть певні SteamVR трекери.
Корисно для ігор або програм, які підтримують лише певні трекери.
settings-general-steamvr-trackers-waist = Талія
settings-general-steamvr-trackers-chest = Груди
settings-general-steamvr-trackers-feet = Ступні
settings-general-steamvr-trackers-knees = Коліна
settings-general-steamvr-trackers-elbows = Лікті
settings-general-steamvr-trackers-hands = Руки
## Tracker mechanics
settings-general-tracker_mechanics = Механіки трекера
settings-general-tracker_mechanics-filtering = Фільтрація
# This also cares about multilines
settings-general-tracker_mechanics-filtering-description =
Виберіть тип фільтрації для своїх трекерів.
Передбачення передбачає рух, а згладжування згладжує рух.
settings-general-tracker_mechanics-filtering-type = Тип фільтрації
settings-general-tracker_mechanics-filtering-type-none = Без фільтрації
settings-general-tracker_mechanics-filtering-type-none-description = Використовуйте обертання як є. Ніякої фільтрації не зробить.
settings-general-tracker_mechanics-filtering-type-smoothing = Згладжування
settings-general-tracker_mechanics-filtering-type-smoothing-description = Згладжує рухи, але додає деяку затримку.
settings-general-tracker_mechanics-filtering-type-prediction = Передбачення
settings-general-tracker_mechanics-filtering-type-prediction-description = Зменшує затримку і робить рухи більш швидкими, але може посилити тремтіння.
settings-general-tracker_mechanics-filtering-amount = Кількість
settings-general-tracker_mechanics-drift_compensation = Компенсація дрейфу
# This cares about multilines
settings-general-tracker_mechanics-drift_compensation-description =
Компенсує дрейф нахилу IMU, застосовуючи зворотне обертання.
Змініть суму компенсації та до того, скільки скидань враховано.
settings-general-tracker_mechanics-drift_compensation-enabled-label = Компенсація дрейфу
settings-general-tracker_mechanics-drift_compensation-amount-label = Сума компенсації
settings-general-tracker_mechanics-drift_compensation-max_resets-label = Використання до x останніх скидань
## FK/Tracking settings
settings-general-fk_settings = Налаштування відстеження
# Floor clip:
# why the name - came from the idea of noclip in video games, but is the opposite where clipping to the floor is a desired feature
# definition - Prevents the foot trackers from going lower than they where when a reset was performed
settings-general-fk_settings-leg_tweak-floor_clip = Прив'язка до підлоги
# Skating correction:
# why the name - without this enabled the feet will often slide across the ground as if your skating across the ground,
# since this largely prevents this it corrects for it hence skating correction (note this may be renamed to sliding correction)
# definition - Guesses when each foot is in contact with the ground and uses that information to improve tracking
settings-general-fk_settings-leg_tweak-skating_correction = корекція ковзання
settings-general-fk_settings-leg_tweak-toe_snap = корекція пальців ноги
settings-general-fk_settings-leg_tweak-foot_plant = корекція ступні
settings-general-fk_settings-leg_tweak-skating_correction-amount = Сила корекції ковзання
settings-general-fk_settings-leg_tweak-skating_correction-description = Корекція ковзання коригує катання на ковзанах, але може знизити точність певних моделей руху. Увімкнувши це, обов'язково повністю скиньте та відкалібруйте у грі.
settings-general-fk_settings-leg_tweak-floor_clip-description = Прив'язка до підлоги може зменшити або навіть прибрати проходження через підлогу. Коли вмикаєте, обов'язково зробіть повне скидання і перекалібровку у грі
settings-general-fk_settings-leg_tweak-toe_snap-description = Корекція пальців ноги намагається вгадати обертання ваших ступень, якщо трекери для них не використовуються
settings-general-fk_settings-leg_tweak-foot_plant-description = Корекція ступні повертає ваші ступні так, щоб вони були паралельні землі при контакті
settings-general-fk_settings-leg_fk = Трекінг ноги
settings-general-fk_settings-arm_fk = Трекінг руки
settings-general-fk_settings-arm_fk-description = Намагатися відстежувати руки за допомогою шолома, навіть якщо є інформація о позиції руки
settings-general-fk_settings-arm_fk-force_arms = Відстеження рук з шолома
settings-general-fk_settings-skeleton_settings = Налаштування скелета
settings-general-fk_settings-skeleton_settings-description = Увімкніть або вимкніть налаштування скелета. Рекомендується залишити їх увімкненими.
settings-general-fk_settings-skeleton_settings-extended_spine = Подовжений хребет
settings-general-fk_settings-skeleton_settings-extended_pelvis = Розширений таз
settings-general-fk_settings-skeleton_settings-extended_knees = Подовжене коліно
settings-general-fk_settings-vive_emulation-title = Емуляція Vive
settings-general-fk_settings-vive_emulation-description = Емуляція проблем з трекером талії, які є у трекерів Vive. Це жарт і погіршує відстеження.
settings-general-fk_settings-vive_emulation-label = Увімкнути емуляцію Vive
## Gesture control settings (tracker tapping)
settings-general-gesture_control = Управління жестами
settings-general-gesture_control-subtitle = Скидання на основі дотику
settings-general-gesture_control-description = Дозволяє запускати скидання, торкнувшись трекера. Трекер найвищий на вашому тулубі використовується для скидання нахилу, трекер найвищий на лівій нозі використовується для повного скидання, а трекер найвищий на правій нозі використовується для скидання закріплення. Слід зазначити, що дотики повинні відбутися протягом 0,6 секунди для реєстрації.
# This is a unit: 3 taps, 2 taps, 1 tap
# $amount (Number) - Amount of taps (touches to the tracker's case)
settings-general-gesture_control-taps =
{ $amount ->
[one] 1 дотик
[few] 2 дотика
[many] { $amount } дотиків
*[other] { $amount } дотиків
}
settings-general-gesture_control-yawResetEnabled = Увімкнути дотик, щоб скинути нахил
settings-general-gesture_control-yawResetDelay = Затримка скидання нахилу
settings-general-gesture_control-yawResetTaps = Дотики для скидання нахилу
settings-general-gesture_control-fullResetEnabled = Увімкнути дотик для повного скидання
settings-general-gesture_control-fullResetDelay = Затримка повного скидання
settings-general-gesture_control-fullResetTaps = Дотики для повного скидання
settings-general-gesture_control-mountingResetEnabled = Увімкнути дотик для скидання прив'язки
settings-general-gesture_control-mountingResetDelay = Затримка скидання прив'язки
settings-general-gesture_control-mountingResetTaps = Дотики для скидання прив'язки
## Interface settings
settings-general-interface = Інтерфейс
settings-general-interface-dev_mode = Режим розробника
settings-general-interface-dev_mode-description = Цей режим може бути корисним, якщо вам потрібні поглиблені дані або для взаємодії з підключеними трекерами на більш просунутому рівні.
settings-general-interface-dev_mode-label = Режим розробника
settings-general-interface-serial_detection = Виявлення послідовного пристрою
settings-general-interface-serial_detection-description = Цей параметр відображатиме спливаюче вікно кожного разу, коли ви підключаєте новий послідовний пристрій, який може бути трекером. Це допомагає покращити процес налаштування трекера.
settings-general-interface-serial_detection-label = Виявлення послідовного пристрою
settings-general-interface-feedback_sound = Звук зворотного зв'язку
settings-general-interface-feedback_sound-description = Ця опція відтворюватиме звуковий сигнал при спрацьовуванні скидання
settings-general-interface-feedback_sound-label = Звук зворотного зв'язку
settings-general-interface-feedback_sound-volume = Гучність звуку зворотного зв'язку
settings-general-interface-theme = Варіація оформлення
settings-general-interface-lang = Виберіть мову
settings-general-interface-lang-description = Змініть мову за замовчуванням, яку ви хочете використовувати.
settings-general-interface-lang-placeholder = Виберіть мову для використання
## Serial settings
settings-serial = Послідовна консоль
# This cares about multilines
settings-serial-description =
Це інформаційна стрічка для послідовного зв'язку.
Може бути корисним, якщо вам потрібно знати, що прошивка не працює.
settings-serial-connection_lost = Підключення до послідовного пристрою втрачене, повторне підключення...
settings-serial-reboot = Перезавантажити
settings-serial-factory_reset = Скидання до заводських налаштувань
# This cares about multilines
# <b>text</b> means that the text should be bold
settings-serial-factory_reset-warning =
<b>Попередження:</b> Це скине трекер до заводських налаштувань.
Це означає, що Wi-Fi та налаштування калібрування <b>будуть втрачені!</b>
settings-serial-factory_reset-warning-ok = Я знаю, що роблю
settings-serial-factory_reset-warning-cancel = Скасувати
settings-serial-get_infos = Отримати інформацію
settings-serial-serial_select = Вибір послідовного порту
settings-serial-auto_dropdown_item = Автоматично
## OSC router settings
settings-osc-router = OSC роутер
# This cares about multilines
settings-osc-router-description =
Пересилання повідомлень OSC з іншої програми.
Корисно для використання іншої програми OSC з VRChat, наприклад.
settings-osc-router-enable = Увімкнути
settings-osc-router-enable-description = Увімкнути пересилання повідомлень.
settings-osc-router-enable-label = Увімкнути
settings-osc-router-network = Мережеві порти
# This cares about multilines
settings-osc-router-network-description =
Встановіть порти для прослуховування і відправки даних.
Вони можуть бути такими ж, як і інші порти, що використовуються на сервері SlimeVR.
settings-osc-router-network-port_in =
.label = Вхідний Порт
.placeholder = Вхідний Порт (зазвичай: 9002)
settings-osc-router-network-port_out =
.label = Вихідний Порт
.placeholder = Вихідний Порт (зазвичай: 9000)
settings-osc-router-network-address = Мережева адреса
settings-osc-router-network-address-description = Укажіть адресу для надсилання даних за адресою.
settings-osc-router-network-address-placeholder = IPV4-адреса
## OSC VRChat settings
settings-osc-vrchat = VRChat OSC трекери
# This cares about multilines
settings-osc-vrchat-description =
Змініть специфічні для VRChat налаштування для отримання даних шолому та надсилання
даних трекерів для FBT без SteamVR (наприклад, автономний Quest).
settings-osc-vrchat-enable = Увімкнути
settings-osc-vrchat-enable-description = Перемикайте відправку та отримання даних.
settings-osc-vrchat-enable-label = Увімкнути
settings-osc-vrchat-network = Мережеві порти
settings-osc-vrchat-network-description = Встановіть порти для прослуховування і відправки даних в VRChat.
settings-osc-vrchat-network-port_in =
.label = Вхідний Порт
.placeholder = Вхідний Порт (зазвичай: 9001)
settings-osc-vrchat-network-port_out =
.label = Вихідний Порт
.placeholder = Вихідний Порт (зазвичай: 9000)
settings-osc-vrchat-network-address = Мережева адреса
settings-osc-vrchat-network-address-description = Виберіть, за якою адресою надсилати дані до VRChat (перевірте налаштування Wi-Fi на своєму пристрої).
settings-osc-vrchat-network-address-placeholder = IP-адреса VRChat
settings-osc-vrchat-network-trackers = Трекери
settings-osc-vrchat-network-trackers-description = Перемикання відправку конкретних трекерів через OSC.
settings-osc-vrchat-network-trackers-chest = Груди
settings-osc-vrchat-network-trackers-hip = Бедро
settings-osc-vrchat-network-trackers-knees = Коліна
settings-osc-vrchat-network-trackers-feet = Ступні
settings-osc-vrchat-network-trackers-elbows = Лікті
## VMC OSC settings
settings-osc-vmc = Віртуальне захоплення руху
# This cares about multilines
settings-osc-vmc-description =
Змінення настройок протоколу VMC (Virtual Motion Capture)
щоб надсилати дані про кістки SlimeVR та отримувати дані про кістки з інших програм.
settings-osc-vmc-enable = Увімкнути
settings-osc-vmc-enable-description = Перемикайте відправку та отримання даних.
settings-osc-vmc-enable-label = Увімкнути
settings-osc-vmc-network = Мережеві порти
settings-osc-vmc-network-description = Встановіть порти для прослуховування і відправки даних по VMC
settings-osc-vmc-network-port_in =
.label = Вхідний Порт
.placeholder = Вхідний Порт (зазвичай: 39540)
settings-osc-vmc-network-port_out =
.label = Вихідний Порт
.placeholder = Вихідний Порт (зазвичай: 39539)
settings-osc-vmc-network-address = Мережева адреса
settings-osc-vmc-network-address-description = Виберіть, за якою адресою надсилати дані через VMC
settings-osc-vmc-network-address-placeholder = IPV4-адреса
settings-osc-vmc-vrm = Модель VRM
settings-osc-vmc-vrm-description = Завантажте модель VRM, щоб дозволити головний якір і забезпечити більш високу сумісність з іншими програмами
settings-osc-vmc-vrm-model_unloaded = Модель не завантажена
settings-osc-vmc-vrm-model_loaded =
{ $titled ->
[true] Модель завантажена: { $name }
*[other] Завантажена модель без назви
}
settings-osc-vmc-vrm-file_select = Перетягніть модель для використання або <u>знайдіть</u>
settings-osc-vmc-anchor_hip = Якір у стегон
settings-osc-vmc-anchor_hip-description = Закріпіть стеження на стегнах, корисно для сидячих VTubing. Якщо вимкнено, завантажте модель VRM.
settings-osc-vmc-anchor_hip-label = Якір у стегон
## Setup/onboarding menu
onboarding-skip = Пропустити налаштування
onboarding-continue = Продовжити
onboarding-wip = В роботі
onboarding-previous_step = Попередній крок
onboarding-setup_warning =
<b>Попередження:</b> Початкова настройка потрібна для хорошого відстеження,
це потрібно, якщо ви вперше використовуєте SlimeVR.
onboarding-setup_warning-skip = Пропустити налаштування
onboarding-setup_warning-cancel = Продовжити налаштування
## Wi-Fi setup
onboarding-wifi_creds-back = Повернутися до вступу
onboarding-wifi_creds = Введіть дані Wi-Fi
# This cares about multilines
onboarding-wifi_creds-description =
Трекери використовуватимуть ці дані для бездротового підключення.
Будь ласка, використовуйте дані, до яких ви зараз підключені.
onboarding-wifi_creds-skip = Пропустити налаштування Wi-Fi
onboarding-wifi_creds-submit = Підтвердити!
onboarding-wifi_creds-ssid =
.label = Назва Wi-Fi
.placeholder = Введіть назву Wi-Fi
onboarding-wifi_creds-password =
.label = Пароль
.placeholder = Введіть Пароль
## Mounting setup
onboarding-reset_tutorial-back = Повернутися до розділу Калібрування прив'язки
onboarding-reset_tutorial = Інструкція по скиданню
onboarding-reset_tutorial-explanation = Коли ви використовуєте свої трекери, вони можуть вийти з вирівнювання через дрейф нахилу IMU або тому, що ви могли їх фізично перемістити. Це можна виправити кількома способами.
onboarding-reset_tutorial-skip = Пропустити крок
# Cares about multiline
onboarding-reset_tutorial-0 =
Торкніться { $taps } виділеного трекера, щоб запустити скидання нахилу.
Це змусить трекери дивитися в тому ж напрямку, що і ваш шолом.
# Cares about multiline
onboarding-reset_tutorial-1 =
Торкніться { $taps } виділеного трекера, щоб ініціювати повне скидання.
Для цього потрібно стояти (i-поза). Існує затримка 3 секунди (налаштовується), перш ніж це дійсно станеться.
Це повністю скидає положення та обертання всіх ваших трекерів. Це має вирішити більшість проблем.
# Cares about multiline
onboarding-reset_tutorial-2 =
Торкніться { $taps } виділеного елемента стеження, щоб активувати скидання прив'язки.
Скидання прив'язки допомагає в тому, як трекери насправді надягнені на вас, тому, якщо ви випадково перемістили їх і змінили місце прикріплення на велику кількість, це допоможе.
Вам потрібно бути в позі, ніби ви катаєтеся на лижах, як показано на майстрі автоматичної прив'язки, і у вас є 3-секундна затримка (налаштовується), перш ніж вона спрацює.
## Setup start
onboarding-home = Ласкаво просимо до SlimeVR
onboarding-home-start = Давайте налаштуємося!
## Enter VR part of setup
onboarding-enter_vr-back = Повернутися до Прив'язки трекерів
onboarding-enter_vr-title = Час вступати у VR!
onboarding-enter_vr-description = Увімкніть усі свої трекери, а потім вступіть у VR!
onboarding-enter_vr-ready = Я готовий
## Setup done
onboarding-done-title = Все готово!
onboarding-done-description = Насолоджуйтесь досвідом трекінгу всього тіла
onboarding-done-close = Закрити налаштування
## Tracker connection setup
onboarding-connect_tracker-back = Повернутися до даних Wi-Fi
onboarding-connect_tracker-title = Підключіть трекери
onboarding-connect_tracker-description-p0 = Тепер перейдемо до найцікавішого, з'єднання усіх трекерів!
onboarding-connect_tracker-description-p1 = Просто підключіть все, що ще не підключено, через USB-порт.
onboarding-connect_tracker-issue-serial = У мене виникли проблеми з підключенням!
onboarding-connect_tracker-usb = USB-трекер
onboarding-connect_tracker-connection_status-none = Шукаємо трекери
onboarding-connect_tracker-connection_status-serial_init = Підключення до послідовного пристрою
onboarding-connect_tracker-connection_status-provisioning = Надсилання даних Wi-Fi
onboarding-connect_tracker-connection_status-connecting = Спроба підключення до Wi-Fi
onboarding-connect_tracker-connection_status-looking_for_server = Шукаю сервер
onboarding-connect_tracker-connection_status-connection_error = Не вдається підключитися до мережі Wi-Fi
onboarding-connect_tracker-connection_status-could_not_find_server = Не вдалося знайти сервер
onboarding-connect_tracker-connection_status-done = Підключено до сервера
# $amount (Number) - Amount of trackers connected (this is a number, but you can use CLDR plural rules for your language)
# More info on https://www.unicode.org/cldr/cldr-aux/charts/22/supplemental/language_plural_rules.html
# English in this case only has 2 plural rules, which are "one" and "other",
# we use 0 in an explicit way because there is no plural rule in english for 0, so we directly say
# if $amount is 0 then we say "No trackers connected"
onboarding-connect_tracker-connected_trackers =
{ $amount ->
[0] Трекери не підключенно
[one] 1 трекер підключенний
[few] { $amount } трекерів підключенно
[many] { $amount } трекерів підключенно
*[other] { $amount } трекерів підключенно
}
onboarding-connect_tracker-next = Я підключив усі свої трекери
## Tracker calibration tutorial
onboarding-calibration_tutorial = Інструкція з калібрування IMU
onboarding-calibration_tutorial-subtitle = Це допоможе зменшити дрейф трекера!
onboarding-calibration_tutorial-description = Кожен раз, коли ви вмикаєте трекери, їм потрібно на мить відпочити на рівній поверхні для калібрування. Давайте зробимо те ж саме, натиснувши кнопку "{ onboarding-calibration_tutorial-calibrate }", <b>не переміщайте їх!</b>
onboarding-calibration_tutorial-calibrate = Я поклав свої трекери на стіл
onboarding-calibration_tutorial-status-waiting = Чекаємо на Вас
onboarding-calibration_tutorial-status-calibrating = Калібрування
onboarding-calibration_tutorial-status-success = Добре!
onboarding-calibration_tutorial-status-error = Трекер переміщено
## Tracker assignment tutorial
onboarding-assignment_tutorial = Як підготувати Slime трекер перед його надяганням
onboarding-assignment_tutorial-first_step = 1. Розмістіть наліпку з частиною тіла (якщо вона у вас є) на трекері відповідно до вашого вибору
# This text has a character limit of around 11 characters, so please keep it short
onboarding-assignment_tutorial-sticker = Наліпка
onboarding-assignment_tutorial-second_step = 2. Прикріпіть ремінь до трекера, зберігаючи гачок і петльову сторону лицьової сторони ремінця в такій орієнтації:
onboarding-assignment_tutorial-second_step-continuation = Гачок і петльова сторона для подовжувача повинні бути в такій орієнтації:
onboarding-assignment_tutorial-done = Я наклеїв наліпки і закріпив ремінці!
## Tracker assignment setup
onboarding-assign_trackers-back = Повернутися до даних Wi-Fi
onboarding-assign_trackers-title = Призначити трекери
onboarding-assign_trackers-description = Давайте виберемо, який трекер куди йде. Натисніть на місце, де ви хочете розмістити трекер
# Look at translation of onboarding-connect_tracker-connected_trackers on how to use plurals
# $assigned (Number) - Trackers that have been assigned a body part
# $trackers (Number) - Trackers connected to the server
onboarding-assign_trackers-assigned =
{ $trackers ->
[one] { $assigned } з 1 трекеру призначенно
[few] { $assigned } з { $trackers } трекерів призначенно
[many] { $assigned } з { $trackers } трекерів призначенно
*[other] { $assigned } з { $trackers } трекерів призначенно
}
onboarding-assign_trackers-advanced = Відобразити розширені розташування призначень
onboarding-assign_trackers-next = Я призначив усі трекери
## Tracker assignment warnings
# Note for devs, number is used for representing boolean states per bit.
# $unassigned (Number) - Bits are based on BodyAssignment.ASSIGNMENT_RULES order
onboarding-assign_trackers-warning-LEFT_FOOT =
{ $unassigned ->
[0] Ліва нога призначенна, але треба ще ліва щиколотка, ліве стегно та на вибір груди, бедро або талія повинні бути призначенні
[1] Ліва ступня призначенна, але в тебе повинно бути ліве стегно і на вибір груди, бедро або талія теж призначенні
[2] Ліва ступня призначенна, але в тебе повинно бути ще ліва щиколотка та на вибір груди, бедро або талія теж призначенні
[3] Ліва ступня призначенна, але тобі ще треба на вибір груди, бедро або талія теж призначенні
[4] Ліва ступня призначенна, але тобі ще потрібно ліва щиколотка і ліве стегно теж призначенні
[5] Ліва ступня призначенна, але тобі ще потрібно ліве стегно теж призначити
[6] Ліва ступня призначенна, але тобі ще треба ліву щиколотку теж призначити
*[other] Ліва ступня призначенна, але тобі ще треба Невідома кількість непризначенних частин тіла теж призначенні
}
## Tracker mounting method choose
onboarding-choose_mounting = Який метод калібрування закріплення використовувати?
# Multiline text
onboarding-choose_mounting-description = Орієнтація кріплення коригується для розміщення трекерів на вашому тілі.
onboarding-choose_mounting-auto_mounting = Автоматична прив'язка
# Italized text
onboarding-choose_mounting-auto_mounting-label = Експериментальний
onboarding-choose_mounting-auto_mounting-description = Це автоматично визначить напрямки прив'язки для всіх ваших трекерів з 2 поз
onboarding-choose_mounting-manual_mounting = Ручна прив'язка
# Italized text
onboarding-choose_mounting-manual_mounting-label = Рекомендується
onboarding-choose_mounting-manual_mounting-description = Це дозволить вибрати напрямок прив'язки вручну для кожного трекера
## Tracker manual mounting setup
onboarding-manual_mounting-back = Повернутися до VR
onboarding-manual_mounting = Ручне закріплення
onboarding-manual_mounting-description = Натисніть на кожен трекер і виберіть, в який бік вони прив'язані
onboarding-manual_mounting-auto_mounting = Автоматична прив'язка
onboarding-manual_mounting-next = Наступний крок
## Tracker automatic mounting setup
onboarding-automatic_mounting-back = Повернутися до VR
onboarding-automatic_mounting-next = Наступний крок
onboarding-automatic_mounting-prev_step = Попередній крок
onboarding-automatic_mounting-done-restart = Спробуйте знову
## Tracker proportions method choose
## Tracker manual proportions setup
## Tracker automatic proportions setup
onboarding-automatic_proportions-prev_step = Попередній крок
onboarding-automatic_proportions-requirements-next = Я ознайомився з вимогами
onboarding-automatic_proportions-start_recording-title = Приготуйтеся рухатися
onboarding-automatic_proportions-start_recording-next = Почати запис
onboarding-automatic_proportions-recording-title = ЗАПИС
onboarding-automatic_proportions-recording-description-p1 = Повторюйте рухи, показані нижче:
# $time (Number) - Seconds left for the automatic calibration recording to finish (max 20)
onboarding-automatic_proportions-recording-timer =
{ $time ->
[one] залишилась { $time } секунда
[few] залишилось { $time } секунди
[many] залишилось { $time } секунд
*[other] залишилось { $time } секунд
}
onboarding-automatic_proportions-verify_results-title = Перевірити результати
onboarding-automatic_proportions-verify_results-processing = Обробка результату
## Home
## Status system

View File

@@ -13,7 +13,7 @@ websocket-connection_lost = 与服务器的连接丢失,正在尝试重新连
## Update notification
version_update-title = 新版本可用:{ $version }
version_update-description = 点击“更新”将为您下载SlimeVR安装程序。
version_update-description = 点击“{ version_update-update }”将为您下载 SlimeVR 安装程序。
version_update-update = 更新
version_update-close = 关闭
@@ -53,8 +53,8 @@ skeleton_bone-NONE = 无
skeleton_bone-HEAD = 头部偏移
skeleton_bone-NECK = 颈部长度
skeleton_bone-torso_group = 躯干长度
skeleton_bone-CHEST = 胸部长度
skeleton_bone-CHEST_OFFSET = 胸部偏移
skeleton_bone-CHEST = 胸部长度
skeleton_bone-WAIST = 腰部长度
skeleton_bone-HIP = 髋部长度
skeleton_bone-HIP_OFFSET = 髋部偏移
@@ -526,7 +526,7 @@ onboarding-wifi_creds-password =
onboarding-reset_tutorial-back = 返回到佩戴校准
onboarding-reset_tutorial = 重置教程
onboarding-reset_tutorial-description = 踪器在使用时可能会由于IMU的偏航漂移而失准,或者可能因为您对它们进行了物理上的移动。您有几种方法来解决这个问题。
onboarding-reset_tutorial-explanation = 踪器在使用时可能会由于IMU的航向角漂移或是因为您移动了它们而失准。您有几种方法来解决这个问题。
onboarding-reset_tutorial-skip = 跳过步骤
# Cares about multiline
onboarding-reset_tutorial-0 =
@@ -597,7 +597,7 @@ onboarding-connect_tracker-next = 所有的追踪器都连接好了
onboarding-calibration_tutorial = IMU校准教程
onboarding-calibration_tutorial-subtitle = 这将有助于减少追踪器漂移!
onboarding-calibration_tutorial-description = 每次开启追踪器时,它们都需要在平坦的表面上放置片刻以进行自校准。你可以通过点击“校准”按钮来手动校准, <b>校准过程中不要移动它们</b>
onboarding-calibration_tutorial-description = 每次开启追踪器时,它们都需要在平坦的表面上放置片刻以进行自校准。你可以通过点击“{ onboarding-calibration_tutorial-calibrate }”按钮来手动校准, <b>校准过程中不要移动追踪器</b>
onboarding-calibration_tutorial-calibrate = 我已经把追踪器放在桌子上了
onboarding-calibration_tutorial-status-waiting = 等待你的操作
onboarding-calibration_tutorial-status-calibrating = 校准中
@@ -701,11 +701,11 @@ onboarding-choose_mounting = 使用哪种方法校准佩戴朝向?
onboarding-choose_mounting-description = 佩戴方向校准用于确定您身上的追踪器的朝向。
onboarding-choose_mounting-auto_mounting = 自动设置佩戴方向
# Italized text
onboarding-choose_mounting-auto_mounting-subtitle = 推荐
onboarding-choose_mounting-auto_mounting-label = 实验功能
onboarding-choose_mounting-auto_mounting-description = 这将需要你做2个动作以自动检测所有追踪器的佩戴方向
onboarding-choose_mounting-manual_mounting = 手动设置佩戴方向
# Italized text
onboarding-choose_mounting-manual_mounting-subtitle = 如果你清楚自己在做什么
onboarding-choose_mounting-manual_mounting-label = 推荐
onboarding-choose_mounting-manual_mounting-description = 这将需要你手动选择每个追踪器的佩戴方向
## Tracker manual mounting setup
@@ -726,7 +726,7 @@ onboarding-automatic_mounting-next = 下一步
onboarding-automatic_mounting-prev_step = 上一步
onboarding-automatic_mounting-done-title = 佩戴方向已校准。
onboarding-automatic_mounting-done-description = 你的佩戴方向校准完成!
onboarding-automatic_mounting-done-restart = 返回以开始
onboarding-automatic_mounting-done-restart = 再试一次
onboarding-automatic_mounting-mounting_reset-title = 佩戴重置
onboarding-automatic_mounting-mounting_reset-step-0 = 1. 双腿弯曲以滑雪的姿势蹲下,上身向前倾斜,手臂弯曲。
onboarding-automatic_mounting-mounting_reset-step-1 = 按下佩戴重置按钮并等待 3 秒钟,然后追踪器的佩戴方向将被重置。
@@ -752,7 +752,7 @@ onboarding-choose_proportions-manual_proportions = 手动调整身体比例
# Italized text
onboarding-choose_proportions-manual_proportions-subtitle = 用于精细调整
onboarding-choose_proportions-manual_proportions-description = 这将需要你手动修改以调整你的身体比例
onboarding-choose_proportions-save = 储存身体比例到文件
onboarding-choose_proportions-export = 导出身体比例
onboarding-choose_proportions-file_type = 身体比例文件
## Tracker manual proportions setup

View File

@@ -13,7 +13,7 @@ websocket-connection_lost = 與伺服器的連線已中斷,正在嘗試重新
## Update notification
version_update-title = 有可用的新版本:{ $version }
version_update-description = 按下「更新」將為您下載 SlimeVR 安裝程式。
version_update-description = 按下「{ version_update-update }」將為您下載 SlimeVR 安裝程式。
version_update-update = 更新
version_update-close = 關閉
@@ -36,6 +36,7 @@ body_part-RIGHT_HAND = 右手
body_part-RIGHT_UPPER_LEG = 右大腿
body_part-RIGHT_LOWER_LEG = 右小腿
body_part-RIGHT_FOOT = 右腳
body_part-UPPER_CHEST = 上胸
body_part-CHEST = 胸部
body_part-WAIST = 腰部
body_part-HIP = 臀部
@@ -53,8 +54,9 @@ skeleton_bone-NONE = 無
skeleton_bone-HEAD = 頭部偏移
skeleton_bone-NECK = 頸部長度
skeleton_bone-torso_group = 軀幹長度
skeleton_bone-CHEST = 胸長度
skeleton_bone-UPPER_CHEST = 胸長度
skeleton_bone-CHEST_OFFSET = 胸部偏移
skeleton_bone-CHEST = 胸部長度
skeleton_bone-WAIST = 腰部長度
skeleton_bone-HIP = 臀部長度
skeleton_bone-HIP_OFFSET = 臀部偏移
@@ -230,6 +232,7 @@ tracker_selection_menu-RIGHT_UPPER_LEG = { -tracker_selection-part }右大腿?
tracker_selection_menu-RIGHT_LOWER_LEG = { -tracker_selection-part }右小腿?
tracker_selection_menu-RIGHT_FOOT = { -tracker_selection-part }右腳?
tracker_selection_menu-RIGHT_CONTROLLER = { -tracker_selection-part }右控制器?
tracker_selection_menu-UPPER_CHEST = { -tracker_selection-part }上胸?
tracker_selection_menu-CHEST = { -tracker_selection-part }胸部?
tracker_selection_menu-WAIST = { -tracker_selection-part }腰部?
tracker_selection_menu-HIP = { -tracker_selection-part }臀部?
@@ -524,7 +527,7 @@ onboarding-wifi_creds-password =
onboarding-reset_tutorial-back = 返回到配戴校正
onboarding-reset_tutorial = 重置教學
onboarding-reset_tutorial-description = 使用追蹤器時會產生偏移,可能是因為慣性測量單元 (IMU) 會出現左右飄移,或是您移動了追蹤器所造成的。您有幾種方法來修正這個問題。
onboarding-reset_tutorial-explanation = 當您使用追蹤器時追蹤器可能會跑位,原因來自於慣性測量單元 (IMU) 產生了左右飄移,或是您移動了追蹤器的實體位置。您有幾種方法來修正這個問題。
onboarding-reset_tutorial-skip = 跳過本步驟
# Cares about multiline
onboarding-reset_tutorial-0 =
@@ -595,7 +598,7 @@ onboarding-connect_tracker-next = 所有的追蹤器都連接好了
onboarding-calibration_tutorial = IMU 校正教學
onboarding-calibration_tutorial-subtitle = 進行這項操作可以有效減少追蹤器發生飄移的機會
onboarding-calibration_tutorial-description = 每次在打開追蹤器的開關時,需要將追蹤器放置在平面一會兒來進行自動校正。您也可以透過按下“校正”按鈕來進行手動校正,<b>校正過程中請勿移動追蹤器</b>。
onboarding-calibration_tutorial-description = 每次在打開追蹤器的開關時,需要將追蹤器放置在平面一會兒來進行自動校正。您也可以透過按下「{ onboarding-calibration_tutorial-calibrate }」按鈕來進行手動校正,<b>校正過程中請勿移動追蹤器</b>。
onboarding-calibration_tutorial-calibrate = 追蹤器已經放置在桌上了
onboarding-calibration_tutorial-status-waiting = 正在等待您完成動作
onboarding-calibration_tutorial-status-calibrating = 校正中
@@ -608,7 +611,7 @@ onboarding-assignment_tutorial = 戴上 Slime 追蹤器前的準備事項
onboarding-assignment_tutorial-first_step = 1. 若有標示身體部位的貼紙,可在您所要分配使用的追蹤器上貼上。
# This text has a character limit of around 11 characters, so please keep it short
onboarding-assignment_tutorial-sticker = 貼紙
onboarding-assignment_tutorial-second_step = 2. 將綁帶有魔鬼氈的一面,依照下圖所示的方向穿過追蹤器:
onboarding-assignment_tutorial-second_step = 2. 將綁帶有魔鬼氈(魔術貼)的一面,依照下圖所示的方向穿過追蹤器:
onboarding-assignment_tutorial-second_step-continuation = 延伸追蹤器應照下圖所示:
onboarding-assignment_tutorial-done = 我把貼紙跟綁帶都弄上了
@@ -699,11 +702,11 @@ onboarding-choose_mounting = 要使用哪一種配戴校正方式?
onboarding-choose_mounting-description = 配戴校正可以校正追蹤器放在身上的位置。
onboarding-choose_mounting-auto_mounting = 自動配戴校正
# Italized text
onboarding-choose_mounting-auto_mounting-subtitle = 推薦使用
onboarding-choose_mounting-auto_mounting-label = 實驗功能
onboarding-choose_mounting-auto_mounting-description = 本選項會透過兩個身體姿勢,判斷所有追蹤器的配戴方位
onboarding-choose_mounting-manual_mounting = 手動配戴校正
# Italized text
onboarding-choose_mounting-manual_mounting-subtitle = 如果你清楚你要做什麼的話
onboarding-choose_mounting-manual_mounting-label = 推薦使用
onboarding-choose_mounting-manual_mounting-description = 本選項可以讓你選擇每個追蹤器的配戴方位
## Tracker manual mounting setup
@@ -724,7 +727,7 @@ onboarding-automatic_mounting-next = 下一步
onboarding-automatic_mounting-prev_step = 上一步
onboarding-automatic_mounting-done-title = 配戴方向已校正。
onboarding-automatic_mounting-done-description = 你的配戴方向校準完成!
onboarding-automatic_mounting-done-restart = 返回以開始
onboarding-automatic_mounting-done-restart = 再試一次
onboarding-automatic_mounting-mounting_reset-title = 配戴重置
onboarding-automatic_mounting-mounting_reset-step-0 = 1. 雙腿彎曲以滑雪的姿勢蹲下,上身向前傾斜,手臂彎曲。
onboarding-automatic_mounting-mounting_reset-step-1 = 2. 按下「配戴重置」按鈕並等待 3 秒鐘,追蹤器的配戴方向將被重置。
@@ -750,7 +753,7 @@ onboarding-choose_proportions-manual_proportions = 手動軀幹比例校正
# Italized text
onboarding-choose_proportions-manual_proportions-subtitle = 適合進行微調
onboarding-choose_proportions-manual_proportions-description = 本選項可以讓你直接修改軀幹比例的設定值
onboarding-choose_proportions-save = 儲存軀幹比例
onboarding-choose_proportions-export = 匯出軀幹比例
onboarding-choose_proportions-file_type = 軀幹比例描述檔
## Tracker manual proportions setup
@@ -779,7 +782,7 @@ onboarding-automatic_proportions-requirements-description =
你需要穿戴上追蹤器與頭戴顯示器。
你的追蹤器與頭戴顯示器都已經連接到 SlimeVR 伺服器。
你的追蹤器與頭戴顯示器在 SlimeVR 伺服器中運作正常。
你的頭戴顯示器會回報定位資料給 SlimeVR 伺服器(通常為執行 SteamVR 並透過 SlimeVR 的 SteamVR 附加元件來連接 SlimeVR
你的頭戴顯示器會回報定位資料給 SlimeVR 伺服器(通常為執行 SteamVR 並透過 SlimeVR 的 SteamVR 驅動程式來連接 SlimeVR
onboarding-automatic_proportions-requirements-next = 我已閱讀使用需求
onboarding-automatic_proportions-start_recording-title = 準備擺動作囉
onboarding-automatic_proportions-start_recording-description = 我們現在要記錄一些特定的姿勢和動作,將會在下一個畫面中提示。當按鈕被按下時,準備好開始!

View File

@@ -14,6 +14,7 @@ import { SlimeVRIcon } from './icon/SlimeVRIcon';
import { UpperArmIcon } from './icon/UpperArmIcon';
import { UpperLegIcon } from './icon/UpperLegIcon';
import { WaistIcon } from './icon/WaistIcon';
import { UpperChestIcon } from './icon/UpperChestIcon';
// All body parts that are right or left, are by default left!
export const mapPart: Record<
@@ -26,6 +27,9 @@ export const mapPart: Record<
currentLocales: string[];
}) => JSX.Element
> = {
[BodyPart.UPPER_CHEST]: ({ width }) => (
<UpperChestIcon width={width}></UpperChestIcon>
),
[BodyPart.CHEST]: ({ width }) => <ChestIcon width={width}></ChestIcon>,
[BodyPart.HEAD]: ({ width }) => <HeadsetIcon width={width}></HeadsetIcon>,
[BodyPart.HIP]: ({ width }) => <HipIcon width={width}></HipIcon>,

View File

@@ -1,6 +1,11 @@
import classNames from 'classnames';
import { useEffect, useState } from 'react';
import { Control, Controller } from 'react-hook-form';
import {
Control,
Controller,
UseFormGetValues,
useWatch,
} from 'react-hook-form';
import { a11yClick } from '../utils/a11y';
export interface DropdownItem {
@@ -17,6 +22,7 @@ export function Dropdown({
display = 'fit',
placeholder,
control,
getValues,
name,
items = [],
}: {
@@ -26,13 +32,29 @@ export function Dropdown({
display?: 'fit' | 'block';
placeholder: string;
control: Control<any>;
getValues: UseFormGetValues<any>;
name: string;
items: DropdownItem[];
}) {
const itemRefs: Record<string, HTMLLIElement> = {};
const [isOpen, setOpen] = useState(false);
const formValue = {
...{ value: useWatch({ control, name }) as string },
...{ value: getValues(name) as string },
};
useEffect(() => {
if (!isOpen) return;
const curItem = itemRefs[formValue.value];
const dropdownParent = curItem
? (curItem.closest('.dropdown-scroll') as HTMLElement | null)
: null;
if (curItem && dropdownParent) {
dropdownParent.scroll({
top: curItem.offsetTop - dropdownParent.offsetHeight / 2,
});
}
function onWheelEvent() {
if (isOpen && !document.querySelector('div.dropdown-scroll:hover')) {
setOpen(false);
@@ -55,9 +77,9 @@ export function Dropdown({
const isInDropdownScroll = document
.querySelector('div.dropdown-scroll')
?.contains(event.target as HTMLDivElement);
const isInDropdown = document
.querySelector('div.dropdown')
?.contains(event.target as HTMLDivElement);
const isInDropdown = !!(event.target as HTMLDivElement).closest(
'.dropdown'
);
if (isOpen && !isInDropdownScroll && !isInDropdown) {
setOpen(false);
}
@@ -139,7 +161,7 @@ export function Dropdown({
<div
className={classNames(
'absolute z-10 rounded shadow min-w-max max-h-[50vh]',
'overflow-y-auto dropdown-scroll',
'overflow-y-auto dropdown-scroll overflow-x-hidden',
display === 'fit' && 'w-fit',
display === 'block' && 'w-full',
direction === 'up' && 'bottom-[45px]',
@@ -151,21 +173,19 @@ export function Dropdown({
alignment === 'left' && 'left-0'
)}
>
<ul className="py-1 text-sm flex flex-col ">
<ul className="py-1 text-sm flex flex-col">
{items.map((item) => (
<li
className={classNames(
'py-2 px-4 min-w-max cursor-pointer pr-2',
'py-2 px-4 min-w-max cursor-pointer',
variant == 'primary' &&
'hover:bg-background-50 text-background-20 hover:text-background-10',
'checked-hover:bg-background-50 text-background-20 ' +
'checked-hover:text-background-10',
variant == 'secondary' &&
'hover:bg-background-60 text-background-20 hover:text-background-10',
'checked-hover:bg-background-60 text-background-20 ' +
'checked-hover:text-background-10',
variant == 'tertiary' &&
value !== item.value &&
'bg-accent-background-30 hover:bg-accent-background-20',
variant == 'tertiary' &&
value === item.value &&
'bg-accent-background-20'
'bg-accent-background-30 checked-hover:bg-accent-background-20'
)}
onClick={() => {
onChange(item.value);
@@ -176,8 +196,10 @@ export function Dropdown({
onChange(item.value);
setOpen(false);
}}
ref={(ref) => (ref ? (itemRefs[item.value] = ref) : {})}
key={item.value}
tabIndex={0}
data-checked={item.value === value}
>
{item.label}
</li>

View File

@@ -15,9 +15,11 @@ export function LangSelector({
const { changeLocales } = useContext(LangContext);
const { l10n } = useLocalization();
const { config, setConfig } = useConfig();
const { control, watch, handleSubmit } = useForm<{ lang: string }>({
defaultValues: { lang: config?.lang || 'en' },
});
const { control, watch, handleSubmit, getValues } = useForm<{ lang: string }>(
{
defaultValues: { lang: config?.lang || 'en' },
}
);
const languagesItems = useMemo(
() => langs.map(({ key, name }) => ({ label: name, value: key })),
@@ -37,6 +39,7 @@ export function LangSelector({
return (
<Dropdown
control={control}
getValues={getValues}
name="lang"
placeholder={l10n.getString(
'settings-general-interface-lang-placeholder'

View File

@@ -15,6 +15,13 @@ export function PersonFrontIcon({ width }: { width?: number }) {
href="/images/front-standing-pose.png"
></image>
{/* <path d="M84.53 224.074C83.953 230.874 88.569 266.874 90.951 280.984C92.085 287.671 95.195 298.565 94.076 304.349C92.476 312.411 92.017 322.843 92.896 328.918C93.451 332.607 95.196 349.618 92.696 355.845C91.389 359.108 88.996 375.832 88.996 375.832C82.756 391.587 86.278 390.812 86.278 390.812C88.21 393.183 91.519 390.998 91.519 390.998C92.1549 391.464 92.9388 391.682 93.7241 391.612C94.5094 391.542 95.2421 391.188 95.785 390.616C97.949 392.407 100.471 390.396 100.471 390.396C103.189 391.807 105.71 389.205 105.71 389.205C107.271 389.991 107.653 388.998 107.653 388.998C112.337 388.698 105.039 373.706 105.039 373.706C103.291 360.242 106.773 352.748 106.773 352.748C118.178 318.926 118.758 309.948 114.199 297.204C112.915 293.524 112.59 292.067 113.181 290.47C114.547 286.783 113.551 271.953 115.217 266.064C118.431 254.706 121.602 225.903 123.254 212.464C125.475 194.364 115.388 170.088 115.388 170.088C113.179 160.21 116.418 125.016 116.418 125.016C120.941 132.054 120.768 144.477 120.768 144.477C120.05 157.506 131.294 177.42 131.294 177.42C136.694 185.649 138.742 193.456 138.742 194.036C138.742 196.407 138.223 202.145 138.223 202.145L138.43 207.145C138.803 209.721 139.034 212.316 139.123 214.918C138.28 227.953 140.35 225.501 140.35 225.501C142.098 225.501 144.018 215.011 144.018 215.011C144.018 217.711 143.357 225.811 144.818 228.869C146.564 232.512 147.848 228.244 147.871 227.387C148.333 210.787 149.33 215.138 149.33 215.138C150.301 228.602 151.494 231.644 153.63 230.591C155.25 229.818 153.769 214.433 153.769 214.433C156.544 223.572 158.649 225.027 158.649 225.027C163.229 228.243 160.397 219.361 159.76 217.602C156.371 208.256 156.267 205.017 156.267 205.017C160.501 213.417 163.692 213.104 163.692 213.104C167.822 211.786 160.083 199.894 155.548 194.197C153.234 191.297 150.248 187.408 149.384 185.097C147.973 181.188 146.907 168.62 146.907 168.62C146.48 153.79 142.813 147.348 142.813 147.348C136.544 137.314 135.365 118.598 135.365 118.598L135.09 87C132.89 65.445 117.01 65.29 117.01 65.29C100.957 62.9 98.723 57.714 98.723 57.714C95.323 52.821 97.266 43.44 97.266 43.44C100.087 41.145 101.175 35.053 101.175 35.053C105.859 31.461 105.63 26.205 103.466 26.262C101.73 26.308 102.123 24.87 102.123 24.87C105.052 1.208 84.046 0 84.046 0H80.836C80.836 0 59.821 1.208 62.746 24.864C62.746 24.864 63.139 26.304 61.388 26.256C59.23 26.199 59.029 31.456 63.696 35.047C63.696 35.047 64.783 41.137 67.605 43.434C67.605 43.434 69.548 52.814 66.148 57.708C66.148 57.708 63.922 62.894 47.861 65.284C47.861 65.284 31.952 65.44 29.788 86.994L29.488 118.594C29.488 118.594 28.331 137.311 22.038 147.344C22.038 147.344 18.389 153.787 17.967 168.616C17.967 168.616 16.898 181.184 15.492 185.093C14.635 187.393 11.653 191.276 9.32001 194.193C4.74601 199.878 -2.94199 211.745 1.17101 213.1C1.17101 213.1 4.37901 213.412 8.59601 205.013C8.59601 205.013 8.50901 208.229 5.12501 217.598C4.46001 219.334 1.63201 228.217 6.21301 225.024C6.21301 225.024 8.33501 223.567 11.093 214.43C11.093 214.43 9.61301 229.815 11.26 230.588C13.412 231.642 14.586 228.599 15.56 215.135C15.56 215.135 16.56 210.787 17.017 227.384C17.04 228.241 18.295 232.509 20.049 228.866C21.529 225.811 20.864 217.727 20.864 215.008C20.864 215.008 22.764 225.498 24.536 225.498C24.536 225.498 26.624 227.95 25.767 214.915C25.628 212.786 26.375 208.415 26.467 207.142L26.667 202.142C26.667 202.142 26.146 196.417 26.146 194.033C26.146 193.442 28.194 185.646 33.594 177.417C33.594 177.417 44.826 157.494 44.103 144.474C44.103 144.474 43.947 132.051 48.47 125.013C48.47 125.013 51.68 160.205 49.505 170.085C49.505 170.085 39.405 194.358 41.629 212.461C43.27 225.937 46.435 254.702 49.657 266.061C51.34 271.938 50.345 286.761 51.693 290.467C52.301 292.076 51.982 293.558 50.675 297.201C46.141 309.947 46.718 318.925 58.123 352.745C58.123 352.745 61.633 360.239 59.859 373.703C59.859 373.703 52.572 388.695 57.239 388.995C57.239 388.995 57.604 389.988 59.182 389.202C59.182 389.202 61.703 391.802 64.427 390.393C64.427 390.393 66.95 392.407 69.106 390.613C69.6451 391.185 70.3751 391.54 71.158 391.61C71.9409 391.681 72.7225 391.462 73.355 390.995C73.355 390.995 76.664 393.227 78.63 390.809C78.63 390.809 82.123 391.584 75.904 375.829C75.904 375.829 73.522 359.129 72.209 355.842C69.709 349.621 71.474 332.57 72.009 328.915C72.87 322.806 72.409 312.398 70.835 304.346C69.684 298.575 72.801 287.679 73.952 280.981C76.317 266.881 80.952 230.881 80.373 224.071L82.288 224.743C83.0863 224.756 83.8692 224.522 84.53 224.074Z" /> */}
<circle
className="body-part-circle"
cx="82"
cy="114"
r={CIRCLE_RADIUS}
id={BodyPart[BodyPart.UPPER_CHEST]}
/>
<circle
className="body-part-circle"
cx="82"

View File

@@ -1,11 +1,27 @@
export function ArrowDownIcon() {
export function ArrowDownIcon({ size = 24 }: { size?: number }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
version="1.1"
width="24"
height="24"
width={size}
height={size}
viewBox="0 0 24 24"
>
<path d="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z" />
</svg>
);
}
export function ArrowUpIcon({ size = 24 }: { size: number }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
version="1.1"
width={size}
height={size}
className="rotate-180"
viewBox="0 0 24 24"
>
<path d="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z" />

View File

@@ -0,0 +1,13 @@
export function UpperChestIcon({ width = 24 }: { width?: number }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
width={width}
viewBox="0 0 50 50"
>
<path d="M 20 2 C 18.156 2 16.700141 3.1345312 15.619141 4.0195312 C 12.698141 4.2755313 4 7.245 4 15 C 4 17.305 4.551 19.068203 4.875 19.908203 C 4.262 20.969203 3 23.679 3 28 C 3 28.352 3.184375 28.677422 3.484375 28.857422 L 8.484375 31.857422 C 8.835375 32.068422 9.2804219 32.044922 9.6074219 31.794922 C 10.290422 31.270922 10.833578 30.512578 11.267578 29.642578 C 11.118578 28.880578 11 28.05 11 27 L 11 21 C 11 20.448 11.448 20 12 20 C 12.43 20 12.790641 20.27425 12.931641 20.65625 C 12.944641 20.68425 12.982 20.797656 13 20.847656 C 13.197 21.404656 14.365 24 19 24 C 23.133 24 23.984531 19.976687 24.019531 19.804688 C 24.113531 19.329687 24.535 19.018531 25 19.019531 C 25.465 19.019531 25.886469 19.330687 25.980469 19.804688 C 26.015469 19.976687 26.867 24 31 24 C 35.635 24 36.803 21.405656 37 20.847656 C 37.018 20.797656 37.055359 20.68425 37.068359 20.65625 C 37.209359 20.27525 37.57 20 38 20 C 38.552 20 39 20.448 39 21 L 39 27 C 39 28.05 38.881422 28.881578 38.732422 29.642578 C 39.166422 30.513578 39.709578 31.269969 40.392578 31.792969 C 40.719578 32.042969 41.164625 32.066469 41.515625 31.855469 L 46.515625 28.855469 C 46.815625 28.676469 47 28.352 47 28 C 47 23.679 45.738 20.969203 45.125 19.908203 C 45.449 19.068203 46 17.305 46 15 C 46 7.245 37.301859 4.2755312 34.380859 4.0195312 C 33.299859 3.1345312 31.844 2 30 2 L 25 2 L 20 2 z M 25 22.609375 C 23.953 24.323375 22.059 26 19 26 C 16.075 26 14.195 25.130766 13 24.134766 L 13 27 C 13 28.281 13.194875 29.155016 13.421875 30.166016 C 13.692875 31.375016 14 32.746 14 35 C 14 35.027 13.987375 35.049172 13.984375 35.076172 C 13.994375 35.366172 14 35.669 14 36 L 14 42 C 14 44.997 18.628641 47.438484 19.556641 47.896484 C 19.693641 47.965484 19.847 48 20 48 L 25 48 L 30 48 C 30.153 48 30.306359 47.965484 30.443359 47.896484 C 31.371359 47.438484 36 44.997 36 42 L 36 36 C 36 35.669 36.005625 35.366172 36.015625 35.076172 C 36.012625 35.049172 36 35.027 36 35 C 36 32.746 36.307125 31.375016 36.578125 30.166016 C 36.805125 29.155016 37 28.281 37 27 L 37 24.134766 C 35.805 25.130766 33.925 26 31 26 C 27.941 26 26.047 24.323375 25 22.609375 z"></path>
</svg>
);
}

View File

@@ -6,7 +6,12 @@ import { BodyInteractions } from '../commons/BodyInteractions';
import { TrackerPartCard } from '../tracker/TrackerPartCard';
import { BodyPartError } from './pages/trackers-assign/TrackerAssignment';
export const SPINE_PARTS = [BodyPart.HIP, BodyPart.CHEST, BodyPart.WAIST];
export const SPINE_PARTS = [
BodyPart.UPPER_CHEST,
BodyPart.CHEST,
BodyPart.WAIST,
BodyPart.HIP,
];
export const ASSIGNMENT_RULES: Partial<
Record<BodyPart, (BodyPart | BodyPart[])[]>
> = {
@@ -26,6 +31,8 @@ export const ASSIGNMENT_RULES: Partial<
[BodyPart.RIGHT_UPPER_LEG]: [SPINE_PARTS],
[BodyPart.HIP]: [BodyPart.CHEST],
[BodyPart.WAIST]: [BodyPart.CHEST],
// TODO chest OR upperChest.
// Also don't warn if no legs.
};
export function BodyAssignment({
@@ -177,6 +184,17 @@ export function BodyAssignment({
}
rightControls={
<div className="flex flex-col justify-between h-full">
{advanced && (
<TrackerPartCard
onlyAssigned={onlyAssigned}
roleError={rolesWithErrors[BodyPart.UPPER_CHEST]?.label}
td={trackerPartGrouped[BodyPart.UPPER_CHEST]}
role={BodyPart.UPPER_CHEST}
onClick={() => onRoleSelected(BodyPart.UPPER_CHEST)}
direction="left"
/>
)}
<TrackerPartCard
onlyAssigned={onlyAssigned}
roleError={rolesWithErrors[BodyPart.CHEST]?.label}

View File

@@ -1,24 +1,39 @@
import { ReactNode } from 'react';
import { ReactNode, useState } from 'react';
import { useLayout } from '../../hooks/layout';
import { useOnboarding } from '../../hooks/onboarding';
import { MainLayoutRoute } from '../MainLayout';
import { TopBar } from '../TopBar';
import { useBreakpoint } from '../../hooks/breakpoint';
import { SkipSetupButton } from './SkipSetupButton';
import { SkipSetupWarningModal } from './SkipSetupWarningModal';
export function OnboardingLayout({ children }: { children: ReactNode }) {
const { layoutHeight, ref } = useLayout<HTMLDivElement>();
const { isMobile } = useBreakpoint('mobile');
const { state } = useOnboarding();
const { state, skipSetup } = useOnboarding();
const [skipWarning, setSkipWarning] = useState(false);
return !state.alonePage ? (
<>
<TopBar progress={state.progress}></TopBar>
<div
ref={ref}
className="flex-grow xs:pt-10 mobile:pt-2"
className="flex-grow relative"
style={{ height: layoutHeight }}
>
<div className="absolute top-12 mobile:top-0 right-2 z-50">
<SkipSetupButton
visible={true}
modalVisible={skipWarning}
onClick={() => setSkipWarning(true)}
></SkipSetupButton>
</div>
{children}
<SkipSetupWarningModal
accept={skipSetup}
onClose={() => setSkipWarning(false)}
isOpen={skipWarning}
></SkipSetupWarningModal>
</div>
</>
) : (

View File

@@ -3,8 +3,6 @@ import { useOnboarding } from '../../../hooks/onboarding';
import { Button } from '../../commons/Button';
import { Typography } from '../../commons/Typography';
import { useMemo, useState } from 'react';
import { SkipSetupWarningModal } from '../SkipSetupWarningModal';
import { SkipSetupButton } from '../SkipSetupButton';
import { ProgressBar } from '../../commons/ProgressBar';
import { LoaderIcon, SlimeState } from '../../commons/icon/LoaderIcon';
import { useCountdown } from '../../../hooks/countdown';
@@ -22,8 +20,7 @@ export const IMU_CALIBRATION_TIME = 4;
export function CalibrationTutorialPage() {
const { l10n } = useLocalization();
const { applyProgress, skipSetup } = useOnboarding();
const [skipWarning, setSkipWarning] = useState(false);
const { applyProgress } = useOnboarding();
const [calibrationStatus, setCalibrationStatus] = useState(
CalibrationStatus.WAITING
);
@@ -72,11 +69,6 @@ export function CalibrationTutorialPage() {
return (
<>
<div className="flex flex-col gap-5 h-full items-center w-full justify-center relative">
<SkipSetupButton
visible={true}
modalVisible={skipWarning}
onClick={() => setSkipWarning(true)}
></SkipSetupButton>
<div className="flex w-full h-full justify-center xs:px-20 mobile:px-5 pb-5 gap-14">
<div className="flex gap-4 self-center mobile:z-10">
<div className="flex flex-col max-w-md gap-3">
@@ -160,11 +152,6 @@ export function CalibrationTutorialPage() {
</div>
</div>
</div>
<SkipSetupWarningModal
accept={skipSetup}
onClose={() => setSkipWarning(false)}
isOpen={skipWarning}
></SkipSetupWarningModal>
</div>
</>
);

View File

@@ -20,8 +20,6 @@ import { ProgressBar } from '../../commons/ProgressBar';
import { TipBox } from '../../commons/TipBox';
import { Typography } from '../../commons/Typography';
import { TrackerCard } from '../../tracker/TrackerCard';
import { SkipSetupWarningModal } from '../SkipSetupWarningModal';
import { SkipSetupButton } from '../SkipSetupButton';
import { useBnoExists } from '../../../hooks/imu-logic';
import { useBreakpoint } from '../../../hooks/breakpoint';
@@ -60,16 +58,13 @@ const statusProgressMap = {
export function ConnectTrackersPage() {
const { isMobile } = useBreakpoint('mobile');
const { l10n } = useLocalization();
const { layoutHeight, ref } = isMobile
? { layoutHeight: 0, ref: undefined }
: useLayout<HTMLDivElement>();
const { layoutHeight, ref } = useLayout<HTMLDivElement>();
const { useConnectedTrackers } = useTrackers();
const { applyProgress, state, skipSetup } = useOnboarding();
const { applyProgress, state } = useOnboarding();
const navigate = useNavigate();
const { sendRPCPacket, useRPCPacket } = useWebsocketAPI();
const [provisioningStatus, setProvisioningStatus] =
useState<WifiProvisioningStatus>(WifiProvisioningStatus.NONE);
const [skipWarning, setSkipWarning] = useState(false);
applyProgress(0.4);
@@ -132,14 +127,9 @@ export function ConnectTrackersPage() {
}, [provisioningStatus]);
return (
<div className="flex flex-col h-full items-center relative overflow-y-auto px-4 pb-4">
<SkipSetupButton
visible={!state.alonePage}
modalVisible={skipWarning}
onClick={() => setSkipWarning(true)}
></SkipSetupButton>
<div className="flex flex-col h-full items-center px-4 pb-4">
<div className="flex gap-10 mobile:flex-col w-full xs:max-w-7xl">
<div className="flex flex-col w-full max-w-sm">
<div className="flex flex-col w-full xs:max-w-sm">
<Typography variant="main-title">
{l10n.getString('onboarding-connect_tracker-title')}
</Typography>
@@ -237,20 +227,24 @@ export function ConnectTrackersPage() {
</Typography>
<div
className="xs:flex-grow xs:overflow-y-scroll"
className="overflow-y-scroll mt-2"
ref={ref}
style={isMobile ? { height: layoutHeight - BOTTOM_HEIGHT } : {}}
style={
isMobile && state.alonePage
? { height: layoutHeight - BOTTOM_HEIGHT }
: { height: layoutHeight }
}
>
<div className="grid lg:grid-cols-2 md:grid-cols-1 gap-2 xs:mx-3 pt-3">
<div className="grid lg:grid-cols-2 md:grid-cols-1 gap-2 pr-1">
{Array.from({
...connectedTrackers,
length: Math.max(connectedTrackers.length, isMobile ? 1 : 20),
length: Math.max(connectedTrackers.length, 1),
}).map((tracker, index) => (
<div key={index}>
{!tracker && (
<div
className={classNames(
'rounded-xl h-16 mobile:animate-pulse',
'rounded-xl h-16 animate-pulse',
state.alonePage
? 'bg-background-80'
: 'bg-background-70'
@@ -270,11 +264,6 @@ export function ConnectTrackersPage() {
</div>
</div>
</div>
<SkipSetupWarningModal
accept={skipSetup}
onClose={() => setSkipWarning(false)}
isOpen={skipWarning}
></SkipSetupWarningModal>
</div>
);
}

View File

@@ -1,28 +1,19 @@
import { useLocalization } from '@fluent/react';
import { useState } from 'react';
import { useOnboarding } from '../../../hooks/onboarding';
import { Button } from '../../commons/Button';
import { SlimeVRIcon } from '../../commons/icon/SimevrIcon';
import { LangSelector } from '../../commons/LangSelector';
import { Typography } from '../../commons/Typography';
import { SkipSetupButton } from '../SkipSetupButton';
import { SkipSetupWarningModal } from '../SkipSetupWarningModal';
export function HomePage() {
const { l10n } = useLocalization();
const { applyProgress, skipSetup } = useOnboarding();
const [skipWarning, setSkipWarning] = useState(false);
const { applyProgress } = useOnboarding();
applyProgress(0.1);
return (
<>
<div className="flex flex-col gap-5 h-full items-center w-full justify-center relative px-4">
<SkipSetupButton
visible={true}
modalVisible={skipWarning}
onClick={() => setSkipWarning(true)}
></SkipSetupButton>
<div className="flex flex-col gap-5 h-full items-center w-full justify-center px-4">
<div className="flex flex-col gap-5 items-center z-10 scale-150 mb-20">
<SlimeVRIcon></SlimeVRIcon>
<Typography variant="mobile-title">
@@ -64,11 +55,6 @@ export function HomePage() {
}}
/>
</div>
<SkipSetupWarningModal
accept={skipSetup}
onClose={() => setSkipWarning(false)}
isOpen={skipWarning}
></SkipSetupWarningModal>
</>
);
}

View File

@@ -3,8 +3,6 @@ import { useOnboarding } from '../../../hooks/onboarding';
import { Button } from '../../commons/Button';
import { Typography } from '../../commons/Typography';
import { useState, useMemo, useEffect } from 'react';
import { SkipSetupWarningModal } from '../SkipSetupWarningModal';
import { SkipSetupButton } from '../SkipSetupButton';
import {
BodyPart,
ResetResponseT,
@@ -23,8 +21,7 @@ import { useBreakpoint } from '../../../hooks/breakpoint';
export function ResetTutorialPage() {
const { isMobile } = useBreakpoint('mobile');
const { l10n } = useLocalization();
const { applyProgress, skipSetup } = useOnboarding();
const [skipWarning, setSkipWarning] = useState(false);
const { applyProgress } = useOnboarding();
const { useAssignedTrackers } = useTrackers();
const { useRPCPacket, sendRPCPacket } = useWebsocketAPI();
const [curIndex, setCurIndex] = useState(0);
@@ -122,18 +119,13 @@ export function ResetTutorialPage() {
return (
<div className="flex flex-col gap-5 h-full items-center w-full xs:justify-center relative overflow-y-auto">
<SkipSetupButton
visible={true}
modalVisible={skipWarning}
onClick={() => setSkipWarning(true)}
></SkipSetupButton>
<div className="flex xs:flex-row mobile:flex-col w-full h-full xs:justify-center xs:px-20 mobile:px-4 gap-8 self-center">
<div className="flex flex-col gap-3 xs:w-96 self-center">
<Typography variant="main-title">
{l10n.getString('onboarding-reset_tutorial')}
</Typography>
<Typography color="secondary">
{l10n.getString('onboarding-reset_tutorial-description')}
{l10n.getString('onboarding-reset_tutorial-explanation')}
</Typography>
<div className="flex">
<Button variant="secondary" to="/onboarding/mounting/choose">
@@ -197,16 +189,16 @@ export function ResetTutorialPage() {
</div>
</div>
</div>
<SkipSetupWarningModal
accept={skipSetup}
onClose={() => setSkipWarning(false)}
isOpen={skipWarning}
></SkipSetupWarningModal>
</div>
);
}
export const TORSO_PARTS = [BodyPart.CHEST, BodyPart.WAIST, BodyPart.HIP];
export const TORSO_PARTS = [
BodyPart.UPPER_CHEST,
BodyPart.CHEST,
BodyPart.WAIST,
BodyPart.HIP,
];
export const LEFT_LEG_PARTS = [
BodyPart.LEFT_UPPER_LEG,
BodyPart.LEFT_LOWER_LEG,

View File

@@ -4,19 +4,15 @@ import { useWifiForm } from '../../../hooks/wifi-form';
import { Button } from '../../commons/Button';
import { Input } from '../../commons/Input';
import { Typography } from '../../commons/Typography';
import { useState } from 'react';
import { SkipSetupWarningModal } from '../SkipSetupWarningModal';
import { SkipSetupButton } from '../SkipSetupButton';
import classNames from 'classnames';
import { useTrackers } from '../../../hooks/tracker';
import { useBnoExists } from '../../../hooks/imu-logic';
export function WifiCredsPage() {
const { l10n } = useLocalization();
const { applyProgress, skipSetup, state } = useOnboarding();
const { applyProgress, state } = useOnboarding();
const { control, handleSubmit, submitWifiCreds, formState } = useWifiForm();
const { useConnectedTrackers } = useTrackers();
const [skipWarning, setSkipWarning] = useState(false);
const connectedTrackers = useConnectedTrackers();
applyProgress(0.2);
@@ -29,11 +25,6 @@ export function WifiCredsPage() {
onSubmit={handleSubmit(submitWifiCreds)}
>
<div className="flex flex-col w-full h-full xs:justify-center items-center relative ">
<SkipSetupButton
visible={true}
modalVisible={skipWarning}
onClick={() => setSkipWarning(true)}
></SkipSetupButton>
<div className="flex mobile:flex-col xs:gap-10 px-4">
<div className="flex flex-col max-w-sm">
<Typography variant="main-title">
@@ -117,11 +108,6 @@ export function WifiCredsPage() {
</div>
</div>
</div>
<SkipSetupWarningModal
accept={skipSetup}
onClose={() => setSkipWarning(false)}
isOpen={skipWarning}
></SkipSetupWarningModal>
</form>
);
}

View File

@@ -2,9 +2,6 @@ import { useLocalization } from '@fluent/react';
import { useOnboarding } from '../../../../hooks/onboarding';
import { Button } from '../../../commons/Button';
import { Typography } from '../../../commons/Typography';
import { useState } from 'react';
import { SkipSetupWarningModal } from '../../SkipSetupWarningModal';
import { SkipSetupButton } from '../../SkipSetupButton';
import { useTrackers } from '../../../../hooks/tracker';
import { useBnoExists } from '../../../../hooks/imu-logic';
import { StickerSlime } from './StickerSlime';
@@ -13,8 +10,7 @@ import { ExtensionArrow } from './ExtensionArrow';
export function AssignmentTutorialPage() {
const { l10n } = useLocalization();
const { applyProgress, skipSetup } = useOnboarding();
const [skipWarning, setSkipWarning] = useState(false);
const { applyProgress } = useOnboarding();
const { useConnectedTrackers } = useTrackers();
const connectedTrackers = useConnectedTrackers();
const bnoExists = useBnoExists(connectedTrackers);
@@ -23,83 +19,71 @@ export function AssignmentTutorialPage() {
return (
<>
<div className="flex flex-col gap-5 h-full items-center w-full justify-center relative">
<SkipSetupButton
visible={true}
modalVisible={skipWarning}
onClick={() => setSkipWarning(true)}
></SkipSetupButton>
<div className="flex flex-col w-full h-full justify-center xs:px-20 gap-3 pb-2">
<div className="overflow-y-auto px-4">
<div className="mb-10 self-center">
<Typography variant="main-title">
{l10n.getString('onboarding-assignment_tutorial')}
</Typography>
<div className="flex flex-col gap-5 h-full items-center overflow-y-auto w-full xs:justify-center relative py-2">
<div className="flex flex-col w-full xs:justify-center xs:px-20 gap-3 pb-2 px-4">
<div className="mb-10 self-center">
<Typography variant="main-title">
{l10n.getString('onboarding-assignment_tutorial')}
</Typography>
</div>
<div className="flex flex-col self-center justify-center gap-5 px-2">
<div className="flex gap-12 xs:flex-row mobile:flex-col self-center justify-center">
<div className="flex flex-col gap-5 xs:w-1/4">
<div>
<Typography variant="section-title">
{l10n.getString(
'onboarding-assignment_tutorial-first_step'
)}
</Typography>
</div>
<div className="stroke-background-10 fill-background-10 flex justify-center">
<StickerSlime width="65%"></StickerSlime>
</div>
</div>
<div className="flex flex-col gap-10 xs:w-1/4">
<div>
<Typography variant="section-title">
{l10n.getString(
'onboarding-assignment_tutorial-second_step'
)}
</Typography>
</div>
<div className="fill-background-10 stroke-background-10 flex justify-center">
<TrackerArrow width="75%"></TrackerArrow>
</div>
<div>
<Typography variant="section-title">
{l10n.getString(
'onboarding-assignment_tutorial-second_step-continuation'
)}
</Typography>
</div>
<div className="fill-background-10 stroke-background-10 flex justify-center">
<ExtensionArrow width="75%"></ExtensionArrow>
</div>
</div>
</div>
<div className="flex flex-col self-center justify-center gap-5 px-2">
<div className="flex gap-12 xs:flex-row mobile:flex-col self-center justify-center">
<div className="flex flex-col gap-5 xs:w-1/4">
<div>
<Typography variant="section-title">
{l10n.getString(
'onboarding-assignment_tutorial-first_step'
)}
</Typography>
</div>
<div className="stroke-background-10 fill-background-10 flex justify-center">
<StickerSlime width="65%"></StickerSlime>
</div>
</div>
<div className="flex flex-col gap-10 xs:w-1/4">
<div>
<Typography variant="section-title">
{l10n.getString(
'onboarding-assignment_tutorial-second_step'
)}
</Typography>
</div>
<div className="fill-background-10 stroke-background-10 flex justify-center">
<TrackerArrow width="75%"></TrackerArrow>
</div>
<div>
<Typography variant="section-title">
{l10n.getString(
'onboarding-assignment_tutorial-second_step-continuation'
)}
</Typography>
</div>
<div className="fill-background-10 stroke-background-10 flex justify-center">
<ExtensionArrow width="75%"></ExtensionArrow>
</div>
</div>
</div>
<div className="flex">
<Button
variant="secondary"
to={
bnoExists
? '/onboarding/calibration-tutorial'
: '/onboarding/wifi-creds'
}
>
{l10n.getString('onboarding-previous_step')}
</Button>
<Button
variant="primary"
to="/onboarding/trackers-assign"
className="ml-auto"
>
{l10n.getString('onboarding-assignment_tutorial-done')}
</Button>
</div>
<div className="flex">
<Button
variant="secondary"
to={
bnoExists
? '/onboarding/calibration-tutorial'
: '/onboarding/wifi-creds'
}
>
{l10n.getString('onboarding-previous_step')}
</Button>
<Button
variant="primary"
to="/onboarding/trackers-assign"
className="ml-auto"
>
{l10n.getString('onboarding-assignment_tutorial-done')}
</Button>
</div>
</div>
</div>
<SkipSetupWarningModal
accept={skipSetup}
onClose={() => setSkipWarning(false)}
isOpen={skipWarning}
></SkipSetupWarningModal>
</div>
</>
);

View File

@@ -1,5 +1,4 @@
import { useLocalization } from '@fluent/react';
import { useState } from 'react';
import { RpcMessage, SkeletonResetAllRequestT } from 'solarxr-protocol';
import {
AutoboneContextC,
@@ -16,16 +15,13 @@ import { PutTrackersOnStep } from './autobone-steps/PutTrackersOn';
import { Recording } from './autobone-steps/Recording';
import { StartRecording } from './autobone-steps/StartRecording';
import { VerifyResultsStep } from './autobone-steps/VerifyResults';
import { SkipSetupWarningModal } from '../../SkipSetupWarningModal';
import { SkipSetupButton } from '../../SkipSetupButton';
import { useCountdown } from '../../../../hooks/countdown';
export function AutomaticProportionsPage() {
const { l10n } = useLocalization();
const { applyProgress, skipSetup, state } = useOnboarding();
const { applyProgress, state } = useOnboarding();
const { sendRPCPacket } = useWebsocketAPI();
const context = useProvideAutobone();
const [skipWarning, setSkipWarning] = useState(false);
const { isCounting, startCountdown, timer } = useCountdown({
onCountdownEnd: () => {
sendRPCPacket(
@@ -39,13 +35,8 @@ export function AutomaticProportionsPage() {
return (
<AutoboneContextC.Provider value={context}>
<div className="flex flex-col gap-5 h-full items-center w-full xs:justify-center relative px-4 pb-4">
<SkipSetupButton
visible={!state.alonePage}
modalVisible={skipWarning}
onClick={() => setSkipWarning(true)}
></SkipSetupButton>
<div className="flex flex-col w-full h-full justify-center max-w-3xl gap-5">
<div className="flex flex-col gap-5 h-full items-center w-full xs:justify-center overflow-y-auto overflow-x-hidden relative px-4 pb-4">
<div className="flex flex-col w-full xs:h-full xs:justify-center max-w-3xl gap-5">
<div className="flex flex-col max-w-lg gap-3">
<Typography variant="main-title">
{l10n.getString('onboarding-automatic_proportions-title')}
@@ -85,11 +76,6 @@ export function AutomaticProportionsPage() {
</Button>
</div>
</div>
<SkipSetupWarningModal
accept={skipSetup}
onClose={() => setSkipWarning(false)}
isOpen={skipWarning}
></SkipSetupWarningModal>
</AutoboneContextC.Provider>
);
}

View File

@@ -1,6 +1,14 @@
import { useLocalization } from '@fluent/react';
import classNames from 'classnames';
import { MouseEventHandler, ReactNode, useEffect } from 'react';
import {
MouseEventHandler,
ReactNode,
useEffect,
useRef,
UIEvent,
MouseEvent,
useMemo,
} from 'react';
import {
LabelType,
ProportionChangeType,
@@ -8,7 +16,7 @@ import {
} from '../../../../hooks/manual-proportions';
import { useLocaleConfig } from '../../../../i18n/config';
import { Typography } from '../../../commons/Typography';
import { useBreakpoint } from '../../../../hooks/breakpoint';
import { ArrowDownIcon, ArrowUpIcon } from '../../../commons/icon/ArrowIcons';
function IncrementButton({
children,
@@ -21,7 +29,8 @@ function IncrementButton({
<div
onClick={onClick}
className={classNames(
'p-3 rounded-lg xs:w-16 xs:h-16 mobile:w-10 flex flex-col justify-center items-center bg-background-60 hover:bg-opacity-50'
'p-3 rounded-lg xs:w-16 xs:h-16 mobile:w-10 flex flex-col justify-center items-center',
'bg-background-60 hover:bg-opacity-50 active:bg-accent-background-30'
)}
>
<Typography variant="mobile-title" bold>
@@ -40,25 +49,25 @@ export function BodyProportions({
type: 'linear' | 'ratio';
variant: 'onboarding' | 'alone';
}) {
const { isMobile } = useBreakpoint('mobile');
const [bodyParts, _ratioMode, currentSelection, dispatch, setRatioMode] =
useManualProportions();
const { currentLocales } = useLocaleConfig();
const { bodyParts, dispatch, state, setRatioMode } = useManualProportions();
const { l10n } = useLocalization();
const { currentLocales } = useLocaleConfig();
const srcollerRef = useRef<HTMLDivElement | null>(null);
const cmFormat = Intl.NumberFormat(currentLocales, {
style: 'unit',
unit: 'centimeter',
maximumFractionDigits: 1,
});
const configFormat = Intl.NumberFormat(currentLocales, {
signDisplay: 'always',
maximumFractionDigits: 1,
});
const percentageFormat = Intl.NumberFormat(currentLocales, {
style: 'percent',
maximumFractionDigits: 1,
});
const configFormat = Intl.NumberFormat(currentLocales, {
signDisplay: 'always',
maximumFractionDigits: 1,
});
useEffect(() => {
if (type === 'linear') {
@@ -68,131 +77,229 @@ export function BodyProportions({
}
}, [type]);
useEffect(() => {
if (srcollerRef.current && bodyParts.length > 0) {
moveToIndex(1);
}
}, [srcollerRef, bodyParts.length]);
const handleUIEvent = (e: UIEvent<HTMLDivElement>) => {
const target = e.target as HTMLDivElement;
const itemHeight = target.offsetHeight / 3;
const atSnappingPoint = target.scrollTop % itemHeight === 0;
if (atSnappingPoint) {
const index = target.scrollTop / itemHeight;
const elem = srcollerRef.current?.childNodes[index + 1] as HTMLDivElement;
const id = elem.getAttribute('itemid');
if (id) selectNew(id);
}
};
const clickPart = (id: string) => (e: MouseEvent<HTMLDivElement>) => {
const target = e.target as HTMLDivElement;
target.scrollIntoView({ behavior: 'smooth', block: 'center' });
selectNew(id);
};
const moveToIndex = (index: number, smooth = true) => {
// We add one because of the offset placeholder
const elem = srcollerRef.current?.childNodes[index + 1] as HTMLDivElement;
elem?.scrollIntoView({
behavior: smooth ? 'smooth' : 'auto',
block: 'center',
});
const id = elem.getAttribute('itemid');
if (id) selectNew(id);
};
const selectNew = (id: string) => {
const part = bodyParts.find(({ label }) => label === id);
if (!part) return;
const { value: originalValue, label, type, ...props } = part;
const value =
'index' in props && props.index !== undefined
? props.bones[props.index].value
: originalValue;
switch (type) {
case LabelType.Bone: {
if (!('bone' in props)) throw 'unreachable';
dispatch({
...props,
label,
value,
type: ProportionChangeType.Bone,
});
break;
}
case LabelType.Group: {
if (!('bones' in props)) throw 'unreachable';
dispatch({
...props,
label,
value,
type: ProportionChangeType.Group,
index: undefined,
parentLabel: label,
});
break;
}
case LabelType.GroupPart: {
if (!('index' in props)) throw 'unreachable';
dispatch({
...props,
label,
// If this isn't done, we are replacing total
// with percentage value
value: originalValue,
type: ProportionChangeType.Group,
index: props.index,
});
}
}
};
const seletedLabel = useMemo(() => {
return bodyParts.find(({ label }) => label === state.currentLabel);
}, [state]);
const move = (action: 'next' | 'prev') => {
const elem = srcollerRef.current?.querySelector(
`div[itemid=${state.currentLabel}]`
);
const moveId = (id: string, elem: HTMLDivElement) => {
elem?.scrollIntoView({
behavior: 'smooth',
block: 'center',
});
selectNew(id);
};
if (action === 'prev') {
const prevElem = elem?.previousSibling as HTMLDivElement;
const prevId = prevElem.getAttribute('itemid');
if (!prevId) return;
moveId(prevId, prevElem);
}
if (action === 'next') {
const nextElem = elem?.nextSibling as HTMLDivElement;
const nextId = nextElem.getAttribute('itemid');
if (!nextId) return;
moveId(nextId, nextElem);
}
};
return (
<div className="relative w-full">
<div
className={classNames(
'flex flex-col xs:overflow-y-scroll xs:overflow-x-hidden xs:max-h-[450px] xs:h-[54vh]',
'w-full px-1 gap-3 xs:gradient-mask-b-90'
)}
>
<>
{bodyParts.map(({ label, type, value: originalValue, ...props }) => {
const value =
'index' in props && props.index !== undefined
? props.bones[props.index].value
: originalValue;
const selected = isMobile || currentSelection.label === label;
const selectNew = () => {
switch (type) {
case LabelType.Bone: {
if (!('bone' in props)) throw 'unreachable';
dispatch({
...props,
label,
value,
type: ProportionChangeType.Bone,
});
break;
}
case LabelType.Group: {
if (!('bones' in props)) throw 'unreachable';
dispatch({
...props,
label,
value,
type: ProportionChangeType.Group,
index: undefined,
parentLabel: label,
});
break;
}
case LabelType.GroupPart: {
if (!('index' in props)) throw 'unreachable';
dispatch({
...props,
label,
// If this isn't done, we are replacing total
// with percentage value
value: originalValue,
type: ProportionChangeType.Group,
index: props.index,
});
(bodyParts.length > 0 && (
<div className="flex w-full gap-3">
<div className="flex items-center mobile:justify-center mobile:flex-col gap-2 my-2">
{!precise && (
<div className="mobile:order-2">
<IncrementButton
onClick={() =>
seletedLabel?.type === LabelType.GroupPart
? dispatch({
type: ProportionChangeType.Ratio,
value: -0.05,
})
: dispatch({
type: ProportionChangeType.Linear,
value: -5,
})
}
>
{configFormat.format(-5)}
</IncrementButton>
</div>
)}
<div className="mobile:order-1">
<IncrementButton
onClick={() =>
seletedLabel?.type === LabelType.GroupPart
? dispatch({
type: ProportionChangeType.Ratio,
value: -0.01,
})
: dispatch({
type: ProportionChangeType.Linear,
value: -1,
})
}
};
>
{configFormat.format(-1)}
</IncrementButton>
</div>
{precise && (
<div className="mobile:order-2">
<IncrementButton
onClick={() =>
seletedLabel?.type === LabelType.GroupPart
? dispatch({
type: ProportionChangeType.Ratio,
value: -0.005,
})
: dispatch({
type: ProportionChangeType.Linear,
value: -0.5,
})
}
>
{configFormat.format(-0.5)}
</IncrementButton>
</div>
)}
</div>
<div className="flex flex-grow flex-col">
<div className="flex justify-center">
<div
onClick={() => move('prev')}
className={classNames(
'h-12 w-32 rounded-lg bg-background-60 flex flex-col justify-center',
'items-center fill-background-10',
srcollerRef?.current?.scrollTop ?? 0 > 0
? 'opacity-100 active:bg-accent-background-30'
: 'opacity-50'
)}
>
<ArrowUpIcon size={32}></ArrowUpIcon>
</div>
</div>
<div
ref={srcollerRef}
onScroll={handleUIEvent}
className="h-60 flex-grow flex-col overflow-y-auto snap-y snap-mandatory snap-always no-scrollbar"
>
<div className="h-20 snap-start "></div>
{bodyParts.map((part) => {
const { label, value: originalValue, type, ...props } = part;
const value =
'index' in props && props.index !== undefined
? props.bones[props.index].value
: originalValue;
return (
<div className="flex" key={label}>
const selected = state.currentLabel === label;
return (
<div
className={classNames(
'flex gap-2 transition-opacity duration-300',
!selected && 'opacity-0 pointer-events-none'
)}
>
{!precise && (
<IncrementButton
onClick={() => {
selectNew();
return type === LabelType.GroupPart
? dispatch({
type: ProportionChangeType.Ratio,
value: -0.05,
})
: dispatch({
type: ProportionChangeType.Linear,
value: -5,
});
}}
>
{configFormat.format(-5)}
</IncrementButton>
)}
<IncrementButton
onClick={() => {
selectNew();
return type === LabelType.GroupPart
? dispatch({
type: ProportionChangeType.Ratio,
value: -0.01,
})
: dispatch({
type: ProportionChangeType.Linear,
value: -1,
});
}}
>
{configFormat.format(-1)}
</IncrementButton>
{precise && (
<IncrementButton
onClick={() => {
selectNew();
return type === LabelType.GroupPart
? dispatch({
type: ProportionChangeType.Ratio,
value: -0.005,
})
: dispatch({
type: ProportionChangeType.Linear,
value: -0.5,
});
}}
>
{configFormat.format(-0.5)}
</IncrementButton>
)}
</div>
<div
className="flex flex-grow flex-col px-2"
onClick={selectNew}
key={label}
itemID={label}
onClick={clickPart(label)}
className="snap-start h-20 flex-col flex justify-center"
>
<div
key={label}
className={classNames(
'p-3 rounded-lg xs:h-16 flex w-full items-center justify-between xs:px-6 mobile:px-3 transition-colors duration-300 bg-background-60',
(selected && 'opacity-100') || 'opacity-50'
'h-16 p-3 rounded-lg flex w-full items-center justify-between px-6 transition-colors',
'duration-300 bg-background-60',
(selected && 'opacity-100') ||
'opacity-50 active:bg-accent-background-30'
)}
>
<Typography variant="section-title" bold>
@@ -211,70 +318,87 @@ export function BodyProportions({
</Typography>
</div>
</div>
<div
className={classNames(
'flex gap-2 transition-opacity duration-300',
!selected && 'opacity-0 pointer-events-none'
)}
>
{precise && (
<IncrementButton
onClick={() => {
selectNew();
return type === LabelType.GroupPart
? dispatch({
type: ProportionChangeType.Ratio,
value: 0.005,
})
: dispatch({
type: ProportionChangeType.Linear,
value: 0.5,
});
}}
>
{configFormat.format(+0.5)}
</IncrementButton>
)}
<IncrementButton
onClick={() => {
selectNew();
return type === LabelType.GroupPart
? dispatch({
type: ProportionChangeType.Ratio,
value: 0.01,
})
: dispatch({
type: ProportionChangeType.Linear,
value: 1,
});
}}
>
{configFormat.format(+1)}
</IncrementButton>
{!precise && (
<IncrementButton
onClick={() => {
selectNew();
return type === LabelType.GroupPart
? dispatch({
type: ProportionChangeType.Ratio,
value: 0.05,
})
: dispatch({
type: ProportionChangeType.Linear,
value: 5,
});
}}
>
{configFormat.format(+5)}
</IncrementButton>
)}
</div>
</div>
);
})}
</>
);
})}
<div className="h-20 snap-start"></div>
</div>
<div className="flex justify-center">
<div
onClick={() => move('next')}
className={classNames(
'h-12 w-32 rounded-lg bg-background-60 flex flex-col justify-center',
'items-center fill-background-10',
srcollerRef?.current?.scrollTop !==
(srcollerRef?.current?.scrollHeight ?? 0) -
(srcollerRef?.current?.offsetHeight ?? 0)
? 'opacity-100 active:bg-accent-background-30'
: 'opacity-50'
)}
>
<ArrowDownIcon size={32}></ArrowDownIcon>
</div>
</div>
</div>
<div className="flex items-center mobile:justify-center mobile:flex-col gap-2">
{precise && (
<div className="mobile:order-2">
<IncrementButton
onClick={() =>
seletedLabel?.type === LabelType.GroupPart
? dispatch({
type: ProportionChangeType.Ratio,
value: 0.005,
})
: dispatch({
type: ProportionChangeType.Linear,
value: 0.5,
})
}
>
{configFormat.format(+0.5)}
</IncrementButton>
</div>
)}
<div className="mobile:order-1">
<IncrementButton
onClick={() =>
seletedLabel?.type === LabelType.GroupPart
? dispatch({
type: ProportionChangeType.Ratio,
value: 0.01,
})
: dispatch({
type: ProportionChangeType.Linear,
value: 1,
})
}
>
{configFormat.format(+1)}
</IncrementButton>
</div>
{!precise && (
<div className="mobile:order-2">
<IncrementButton
onClick={() =>
seletedLabel?.type === LabelType.GroupPart
? dispatch({
type: ProportionChangeType.Ratio,
value: 0.05,
})
: dispatch({
type: ProportionChangeType.Linear,
value: 5,
})
}
>
{configFormat.format(+5)}
</IncrementButton>
</div>
)}
</div>
</div>
</div>
)) || <></>
);
}

View File

@@ -8,9 +8,7 @@ import { PersonFrontIcon } from '../../../commons/PersonFrontIcon';
import { Typography } from '../../../commons/Typography';
import { BodyProportions } from './BodyProportions';
import { useLocalization } from '@fluent/react';
import { useEffect, useMemo, useState } from 'react';
import { SkipSetupWarningModal } from '../../SkipSetupWarningModal';
import { SkipSetupButton } from '../../SkipSetupButton';
import { useEffect, useMemo } from 'react';
import { useBreakpoint } from '../../../../hooks/breakpoint';
export function ButtonsControl() {
@@ -49,8 +47,7 @@ export function ButtonsControl() {
export function ManualProportionsPage() {
const { isMobile } = useBreakpoint('mobile');
const { l10n } = useLocalization();
const { applyProgress, skipSetup, state } = useOnboarding();
const [skipWarning, setSkipWarning] = useState(false);
const { applyProgress, state } = useOnboarding();
applyProgress(0.9);
@@ -68,14 +65,9 @@ export function ManualProportionsPage() {
return (
<>
<div className="flex flex-col gap-5 h-full items-center w-full xs:justify-center overflow-y-auto relative">
<SkipSetupButton
visible={!state.alonePage}
modalVisible={skipWarning}
onClick={() => setSkipWarning(true)}
></SkipSetupButton>
<div className="flex flex-col w-full h-full xs:max-w-5xl xs:justify-center">
<div className="flex gap-8 justify-center">
<div className="flex flex-col w-full xs:max-w-2xl gap-3 items-center">
<div className="flex gap-8 justify-center h-full xs:items-center">
<div className="flex flex-col w-full xs:max-w-2xl gap-3 items-center mobile:justify-around">
<div className="flex flex-col">
<Typography variant="main-title">
{l10n.getString('onboarding-manual_proportions-title')}
@@ -100,29 +92,25 @@ export function ManualProportionsPage() {
</div>
)}
</div>
<BodyProportions
precise={precise}
type={ratio ? 'ratio' : 'linear'}
variant={state.alonePage ? 'alone' : 'onboarding'}
></BodyProportions>
<div className="w-full px-2">
<BodyProportions
precise={precise}
type={ratio ? 'ratio' : 'linear'}
variant={state.alonePage ? 'alone' : 'onboarding'}
></BodyProportions>
</div>
</div>
<div className="flex-col flex-grow gap-3 rounded-xl fill-background-50 items-center hidden md:flex">
<PersonFrontIcon width={200}></PersonFrontIcon>
</div>
</div>
{!isMobile && (
<div className="flex gap-3 mt-5 mx-4">
<div className="flex gap-3 my-5 mx-4 justify-between">
<ButtonsControl></ButtonsControl>
</div>
)}
</div>
</div>
<SkipSetupWarningModal
accept={skipSetup}
onClose={() => setSkipWarning(false)}
isOpen={skipWarning}
></SkipSetupWarningModal>
</>
);
}

View File

@@ -1,8 +1,6 @@
import { useOnboarding } from '../../../../hooks/onboarding';
import { useLocalization } from '@fluent/react';
import { useState } from 'react';
import { SkipSetupWarningModal } from '../../SkipSetupWarningModal';
import { SkipSetupButton } from '../../SkipSetupButton';
import classNames from 'classnames';
import { Typography } from '../../../commons/Typography';
import { Button } from '../../../commons/Button';
@@ -20,8 +18,7 @@ import { useIsTauri } from '../../../../hooks/breakpoint';
export function ProportionsChoose() {
const isTauri = useIsTauri();
const { l10n } = useLocalization();
const { applyProgress, skipSetup, state } = useOnboarding();
const [skipWarning, setSkipWarning] = useState(false);
const { applyProgress, state } = useOnboarding();
const { useRPCPacket, sendRPCPacket } = useWebsocketAPI();
const [animated, setAnimated] = useState(false);
@@ -58,11 +55,6 @@ export function ProportionsChoose() {
return (
<>
<div className="flex flex-col gap-5 h-full items-center w-full xs:justify-center mobile:overflow-y-auto relative px-4 pb-4">
<SkipSetupButton
visible={!state.alonePage}
modalVisible={skipWarning}
onClick={() => setSkipWarning(true)}
></SkipSetupButton>
<div className="flex flex-col gap-4 justify-center">
<Typography variant="main-title">
{l10n.getString('onboarding-choose_proportions')}
@@ -186,16 +178,11 @@ export function ProportionsChoose() {
)
}
>
{l10n.getString('onboarding-choose_proportions-save')}
{l10n.getString('onboarding-choose_proportions-export')}
</Button>
</div>
</div>
</div>
<SkipSetupWarningModal
accept={skipSetup}
onClose={() => setSkipWarning(false)}
isOpen={skipWarning}
></SkipSetupWarningModal>
</>
);
}

View File

@@ -6,9 +6,6 @@ import { MountingResetStep } from './mounting-steps/MountingReset';
import { PreparationStep } from './mounting-steps/Preparation';
import { PutTrackersOnStep } from './mounting-steps/PutTrackersOn';
import { useLocalization } from '@fluent/react';
import { SkipSetupWarningModal } from '../../SkipSetupWarningModal';
import { useState } from 'react';
import { SkipSetupButton } from '../../SkipSetupButton';
const steps: Step[] = [
{ type: 'numbered', component: PutTrackersOnStep },
@@ -18,19 +15,13 @@ const steps: Step[] = [
];
export function AutomaticMountingPage() {
const { l10n } = useLocalization();
const { applyProgress, skipSetup, state } = useOnboarding();
const [skipWarning, setSkipWarning] = useState(false);
const { applyProgress, state } = useOnboarding();
applyProgress(0.7);
return (
<>
<div className="flex flex-col gap-2 h-full items-center w-full xs:justify-center relative overflow-y-auto overflow-x-hidden px-4 pb-4">
<SkipSetupButton
visible={!state.alonePage}
modalVisible={skipWarning}
onClick={() => setSkipWarning(true)}
></SkipSetupButton>
<div className="flex flex-col w-full h-full xs:justify-center xs:max-w-3xl gap-5">
<div className="flex flex-col xs:max-w-lg gap-3">
<Typography variant="main-title">
@@ -48,11 +39,6 @@ export function AutomaticMountingPage() {
</div>
</div>
</div>
<SkipSetupWarningModal
accept={skipSetup}
onClose={() => setSkipWarning(false)}
isOpen={skipWarning}
></SkipSetupWarningModal>
</>
);
}

View File

@@ -11,16 +11,13 @@ import { Typography } from '../../../commons/Typography';
import { BodyAssignment } from '../../BodyAssignment';
import { MountingSelectionMenu } from './MountingSelectionMenu';
import { useLocalization } from '@fluent/react';
import { SkipSetupWarningModal } from '../../SkipSetupWarningModal';
import { SkipSetupButton } from '../../SkipSetupButton';
import { useBreakpoint } from '../../../../hooks/breakpoint';
export function ManualMountingPage() {
const { isMobile } = useBreakpoint('mobile');
const { l10n } = useLocalization();
const { applyProgress, skipSetup, state } = useOnboarding();
const { applyProgress, state } = useOnboarding();
const { sendRPCPacket } = useWebsocketAPI();
const [skipWarning, setSkipWarning] = useState(false);
const [selectedRole, setSelectRole] = useState<BodyPart>(BodyPart.NONE);
@@ -70,11 +67,6 @@ export function ManualMountingPage() {
onDirectionSelected={onDirectionSelected}
></MountingSelectionMenu>
<div className="flex flex-col gap-5 h-full items-center w-full xs:justify-center relative overflow-y-auto">
<SkipSetupButton
visible={!state.alonePage}
modalVisible={skipWarning}
onClick={() => setSkipWarning(true)}
></SkipSetupButton>
<div className="flex xs:flex-row mobile:flex-col h-full px-8 xs:w-full xs:justify-center mobile:px-4 items-center">
<div className="flex flex-col w-full xs:max-w-sm gap-3">
<Typography variant="main-title">
@@ -109,11 +101,6 @@ export function ManualMountingPage() {
</div>
</div>
</div>
<SkipSetupWarningModal
accept={skipSetup}
onClose={() => setSkipWarning(false)}
isOpen={skipWarning}
></SkipSetupWarningModal>
</>
);
}

View File

@@ -2,7 +2,6 @@ import { useOnboarding } from '../../../../hooks/onboarding';
import { useLocalization } from '@fluent/react';
import { useState } from 'react';
import { SkipSetupWarningModal } from '../../SkipSetupWarningModal';
import { SkipSetupButton } from '../../SkipSetupButton';
import classNames from 'classnames';
import { Typography } from '../../../commons/Typography';
import { Button } from '../../../commons/Button';
@@ -18,11 +17,6 @@ export function MountingChoose() {
return (
<>
<div className="flex flex-col gap-5 h-full items-center w-full xs:justify-center relative overflow-y-auto px-4 pb-4">
<SkipSetupButton
visible={!state.alonePage}
modalVisible={skipWarning}
onClick={() => setSkipWarning(true)}
></SkipSetupButton>
<div className="flex flex-col gap-4 justify-center">
<div className="xs:w-10/12 xs:max-w-[666px]">
<Typography variant="main-title">
@@ -58,7 +52,7 @@ export function MountingChoose() {
</Typography>
<Typography variant="vr-accessible" italic>
{l10n.getString(
'onboarding-choose_mounting-auto_mounting-subtitle'
'onboarding-choose_mounting-auto_mounting-label'
)}
</Typography>
</div>
@@ -106,7 +100,7 @@ export function MountingChoose() {
</Typography>
<Typography variant="vr-accessible" italic>
{l10n.getString(
'onboarding-choose_mounting-manual_mounting-subtitle'
'onboarding-choose_mounting-manual_mounting-label'
)}
</Typography>
</div>

View File

@@ -26,8 +26,6 @@ import { Typography } from '../../../commons/Typography';
import { ASSIGNMENT_RULES, BodyAssignment } from '../../BodyAssignment';
import { NeckWarningModal } from '../../NeckWarningModal';
import { TrackerSelectionMenu } from './TrackerSelectionMenu';
import { SkipSetupWarningModal } from '../../SkipSetupWarningModal';
import { SkipSetupButton } from '../../SkipSetupButton';
import { useConfig } from '../../../../hooks/config';
import { playTapSetupSound } from '../../../../sounds/sounds';
import { useBreakpoint } from '../../../../hooks/breakpoint';
@@ -48,7 +46,7 @@ export function TrackersAssignPage() {
const { isMobile } = useBreakpoint('mobile');
const { l10n } = useLocalization();
const { useAssignedTrackers, trackers } = useTrackers();
const { applyProgress, skipSetup, state } = useOnboarding();
const { applyProgress, state } = useOnboarding();
const { sendRPCPacket, useRPCPacket } = useWebsocketAPI();
const { control, watch } = useForm<{ advanced: boolean }>({
@@ -57,7 +55,6 @@ export function TrackersAssignPage() {
const { advanced } = watch();
const [selectedRole, setSelectRole] = useState<BodyPart>(BodyPart.NONE);
const assignedTrackers = useAssignedTrackers();
const [skipWarning, setSkipWarning] = useState(false);
const { config } = useConfig();
const [tapDetectionSettings, setTapDetectionSettings] = useState<Omit<
@@ -255,13 +252,6 @@ export function TrackersAssignPage() {
onClose={() => closeChokerWarning(true)}
accept={() => closeChokerWarning(false)}
></NeckWarningModal>
<div className="relative mx-4 top-4">
<SkipSetupButton
visible={!state.alonePage}
modalVisible={skipWarning}
onClick={() => setSkipWarning(true)}
></SkipSetupButton>
</div>
<div className="flex flex-col gap-5 h-full items-center w-full justify-center">
<div className="flex flex-col w-full overflow-y-auto px-4 xs:items-center">
<div className="flex mobile:flex-col md:gap-8 mobile:gap-4 mobile:pb-4">
@@ -330,11 +320,6 @@ export function TrackersAssignPage() {
</div>
</div>
</div>
<SkipSetupWarningModal
accept={skipSetup}
onClose={() => setSkipWarning(false)}
isOpen={skipWarning}
></SkipSetupWarningModal>
</>
);
}

View File

@@ -38,7 +38,7 @@ export function SettingSelectorMobile() {
},
];
const { control, watch, handleSubmit, setValue } = useForm<{
const { control, watch, handleSubmit, setValue, getValues } = useForm<{
link: string;
}>({
defaultValues: { link: links[0].value.url },
@@ -65,6 +65,7 @@ export function SettingSelectorMobile() {
<div className="fixed top-12 z-50 px-4 w-full">
<Dropdown
control={control}
getValues={getValues}
display="block"
items={links.map(({ label, value: { url: value } }) => ({
label,

View File

@@ -17,9 +17,19 @@ export function SettingsPageLayout({
if (!pageRef.current || !typedState || !typedState.scrollTo) {
return;
}
const elem = pageRef.current.querySelector(`#${typedState.scrollTo}`);
const elem = pageRef.current.querySelector(
`#${typedState.scrollTo}`
) as HTMLElement | null;
if (elem) {
elem.scrollIntoView({ behavior: 'smooth', block: 'start' });
// stupid way of doing this, just get the closest overflow-y-auto
// usually its just the parentElem
const closestScroll = elem.closest(
'.overflow-y-auto'
) as HTMLElement | null;
if (closestScroll) {
// The 45 is just enough padding for making the scroll look perfect
closestScroll.scroll({ top: elem.offsetTop - 45, behavior: 'smooth' });
}
}
}, [state]);

View File

@@ -51,9 +51,10 @@ export function Serial() {
const [tryFactoryReset, setTryFactoryReset] = useState(false);
const { control, watch, handleSubmit, reset } = useForm<SerialForm>({
defaultValues: { port: 'Auto' },
});
const { control, watch, handleSubmit, reset, getValues } =
useForm<SerialForm>({
defaultValues: { port: 'Auto' },
});
const { port } = watch();
@@ -234,6 +235,7 @@ export function Serial() {
{isMobile && (
<Dropdown
control={control}
getValues={getValues}
name="port"
display="block"
placeholder={l10n.getString(
@@ -250,6 +252,7 @@ export function Serial() {
{!isMobile && (
<Dropdown
control={control}
getValues={getValues}
name="port"
display="fit"
placeholder={l10n.getString('settings-serial-serial_select')}

View File

@@ -64,7 +64,7 @@ export interface BoneState {
type: BoneType.Single;
bone: SkeletonBone;
value: number;
label: string;
currentLabel: string;
}
export interface GroupState {
@@ -77,7 +77,7 @@ export interface GroupState {
value: number;
}[];
value: number;
label: string;
currentLabel: string;
index?: number;
parentLabel: string;
}
@@ -87,6 +87,7 @@ function reducer(state: ProportionState, action: ProportionChange): ProportionSt
case ProportionChangeType.Bone: {
return {
...action,
currentLabel: action.label,
type: BoneType.Single,
};
}
@@ -94,6 +95,7 @@ function reducer(state: ProportionState, action: ProportionChange): ProportionSt
case ProportionChangeType.Group: {
return {
...action,
currentLabel: action.label,
type: BoneType.Group,
};
}
@@ -114,7 +116,7 @@ function reducer(state: ProportionState, action: ProportionChange): ProportionSt
case ProportionChangeType.Ratio: {
if (state.type === BoneType.Single || state.index === undefined) {
throw new Error(`Unexpected increase of bone ${state.label}`);
throw new Error(`Unexpected increase of bone ${state.currentLabel}`);
}
const newState: GroupState = JSON.parse(JSON.stringify(state));
@@ -182,7 +184,12 @@ export interface GroupPartLabel {
const BONE_MAPPING: Map<string, SkeletonBone[]> = new Map([
[
'skeleton_bone-torso_group',
[SkeletonBone.CHEST, SkeletonBone.HIP, SkeletonBone.WAIST],
[
SkeletonBone.UPPER_CHEST,
SkeletonBone.CHEST,
SkeletonBone.HIP,
SkeletonBone.WAIST,
],
],
['skeleton_bone-leg_group', [SkeletonBone.UPPER_LEG, SkeletonBone.LOWER_LEG]],
['skeleton_bone-arm_group', [SkeletonBone.UPPER_ARM, SkeletonBone.LOWER_ARM]],
@@ -192,16 +199,16 @@ export const INVALID_BONE: BoneState = {
type: BoneType.Single,
bone: SkeletonBone.NONE,
value: 0,
label: 'invalid-bone',
currentLabel: 'invalid-bone',
};
export function useManualProportions(): [
Label[],
boolean,
ProportionState,
(change: ProportionChange) => void,
(ratio: boolean) => void
] {
export function useManualProportions(): {
bodyParts: Label[];
ratioMode: boolean;
state: ProportionState;
dispatch: (change: ProportionChange) => void;
setRatioMode: (ratio: boolean) => void;
} {
const { useRPCPacket, sendRPCPacket } = useWebsocketAPI();
const [config, setConfig] = useState<Omit<SkeletonConfigResponseT, 'pack'> | null>(
null
@@ -278,6 +285,7 @@ export function useManualProportions(): [
useLayoutEffect(() => {
dispatch({
...INVALID_BONE,
label: INVALID_BONE.currentLabel,
type: ProportionChangeType.Bone,
});
}, [ratio]);
@@ -315,7 +323,7 @@ export function useManualProportions(): [
const part = bodyParts.find(
(it) =>
it.type === LabelType.Group &&
(it.label === state.label || it.label === state.parentLabel)
(it.label === state.currentLabel || it.label === state.parentLabel)
) as GroupLabel | undefined;
// Check if we found the group we were looking for
@@ -345,7 +353,7 @@ export function useManualProportions(): [
setConfig(conf);
}, [state]);
return [bodyParts, ratio, state, dispatch, setRatio];
return { bodyParts, ratioMode: ratio, state, dispatch, setRatioMode: setRatio };
}
function roundedStep(value: number, step: number, add: boolean): number {

View File

@@ -65,7 +65,7 @@ export const langs = [
key: 'ko',
},
{
name: '🇳🇴 Norsk bokmål',
name: '🇳🇴 Norsk bokmål',
key: 'nb-NO',
},
{
@@ -84,6 +84,10 @@ export const langs = [
name: '🇷🇺 Русский',
key: 'ru',
},
{
name: '🇺🇦 Українська',
key: 'uk',
},
{
name: '🇻🇳 Tiếng Việt',
key: 'vi',

View File

@@ -257,6 +257,17 @@ body {
height: 100%;
}
/* Hide scrollbar for Chrome, Safari and Opera */
.no-scrollbar::-webkit-scrollbar {
display: none;
}
/* Hide scrollbar for IE, Edge and Firefox */
.no-scrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
::-webkit-scrollbar {
width: 8px;
height: 8px;

View File

@@ -230,6 +230,9 @@ const config = {
'trans-flag': `linear-gradient(135deg, ${colors['trans-blue'][800]} 40%, ${colors['trans-blue'][700]} 40% 70%, ${colors['trans-blue'][600]} 70% 100%)`,
},
},
data: {
checked: 'checked=true'
}
},
plugins: [
forms,
@@ -250,6 +253,9 @@ const config = {
'.text-standard-bold': textConfig(rem(12), 700),
});
}),
plugin(function({ addVariant }) {
addVariant('checked-hover', ['&:hover', '&[data-checked=true]'])
})
],
} satisfies Config

View File

@@ -6,7 +6,6 @@ import dev.slimevr.autobone.errors.*
import dev.slimevr.config.AutoBoneConfig
import dev.slimevr.poseframeformat.PoseFrameIO
import dev.slimevr.poseframeformat.PoseFrames
import dev.slimevr.poseframeformat.player.TrackerFramesPlayer
import dev.slimevr.tracking.processor.BoneType
import dev.slimevr.tracking.processor.HumanPoseManager
import dev.slimevr.tracking.processor.config.SkeletonConfigManager
@@ -32,6 +31,7 @@ class AutoBone(server: VRServer) {
arrayOf(
BoneType.HEAD,
BoneType.NECK,
BoneType.UPPER_CHEST,
BoneType.CHEST,
BoneType.WAIST,
BoneType.HIP, // This now works when using body proportion error! It's not the
@@ -44,6 +44,7 @@ class AutoBone(server: VRServer) {
val heightOffsets = FastList(
arrayOf(
BoneType.NECK,
BoneType.UPPER_CHEST,
BoneType.CHEST,
BoneType.WAIST,
BoneType.HIP,
@@ -68,14 +69,14 @@ class AutoBone(server: VRServer) {
var heightError = HeightError()
var positionError = PositionError()
var positionOffsetError = PositionOffsetError()
// #endregion
private val rand = Random()
val config: AutoBoneConfig
val globalConfig: AutoBoneConfig
init {
config = server.configManager.vrConfig.autoBone
globalConfig = server.configManager.vrConfig.autoBone
this.server = server
loadConfigValues()
}
@@ -87,6 +88,7 @@ class AutoBone(server: VRServer) {
return when (bone) {
BoneType.HEAD -> getOffset.apply(SkeletonConfigOffsets.HEAD)
BoneType.NECK -> getOffset.apply(SkeletonConfigOffsets.NECK)
BoneType.UPPER_CHEST -> getOffset.apply(SkeletonConfigOffsets.UPPER_CHEST)
BoneType.CHEST -> getOffset.apply(SkeletonConfigOffsets.CHEST)
BoneType.WAIST -> getOffset.apply(SkeletonConfigOffsets.WAIST)
BoneType.HIP -> getOffset.apply(SkeletonConfigOffsets.HIP)
@@ -112,7 +114,7 @@ class AutoBone(server: VRServer) {
offsets.clear()
// Get current or default skeleton configs
val skeleton = humanPoseManager
val skeleton = server.humanPoseManager
val getOffset: Function<SkeletonConfigOffsets, Float> =
if (skeleton != null) {
Function { key: SkeletonConfigOffsets -> skeleton.getOffset(key) }
@@ -160,17 +162,6 @@ class AutoBone(server: VRServer) {
return dot2 - dot1
}
private val humanPoseManager: HumanPoseManager?
/**
* A simple utility method to get the [HumanSkeleton] from the
* [VRServer]
*
* @return The [HumanSkeleton] associated with the [VRServer],
* or null if there is none available
* @see {@link VRServer}, {@link HumanSkeleton}
*/
get() = server.humanPoseManager
fun applyConfig(
configConsumer: BiConsumer<SkeletonConfigOffsets, Float>,
offsets: Map<BoneType, Float> = this.offsets,
@@ -184,9 +175,14 @@ class AutoBone(server: VRServer) {
if (neckOffset != null) {
configConsumer.accept(SkeletonConfigOffsets.NECK, neckOffset)
}
val upperChestOffset = offsets[BoneType.UPPER_CHEST]
val chestOffset = offsets[BoneType.CHEST]
val waistOffset = offsets[BoneType.WAIST]
val hipOffset = offsets[BoneType.HIP]
if (upperChestOffset != null) {
configConsumer
.accept(SkeletonConfigOffsets.UPPER_CHEST, upperChestOffset)
}
if (chestOffset != null) {
configConsumer
.accept(SkeletonConfigOffsets.CHEST, chestOffset)
@@ -246,7 +242,7 @@ class AutoBone(server: VRServer) {
}
@JvmOverloads
fun applyAndSaveConfig(humanPoseManager: HumanPoseManager? = this.humanPoseManager): Boolean {
fun applyAndSaveConfig(humanPoseManager: HumanPoseManager? = this.server.humanPoseManager): Boolean {
if (humanPoseManager == null) return false
if (!applyConfig(humanPoseManager)) return false
humanPoseManager.saveConfig()
@@ -310,10 +306,13 @@ class AutoBone(server: VRServer) {
return length
}
fun getTargetHeight(frames: PoseFrames): Float {
fun calcTargetHmdHeight(
frames: PoseFrames,
config: AutoBoneConfig = globalConfig,
): Float {
val targetHeight: Float
// Get the current skeleton from the server
val humanPoseManager = humanPoseManager
val humanPoseManager = server.humanPoseManager
if (config.useSkeletonHeight && humanPoseManager != null) {
// If there is a skeleton available, calculate the target height
// from its configs
@@ -344,240 +343,289 @@ class AutoBone(server: VRServer) {
@Throws(AutoBoneException::class)
fun processFrames(
frames: PoseFrames,
calcInitError: Boolean = true,
targetHeight: Float = -1f,
epochCallback: Consumer<Epoch?>?,
config: AutoBoneConfig = globalConfig,
epochCallback: Consumer<Epoch?>? = null,
): AutoBoneResults {
var targetHeight = targetHeight
val frameCount = frames.maxFrameCount
val frames1 = TrackerFramesPlayer(frames)
val frames2 = TrackerFramesPlayer(frames)
val trackers1 = frames1.trackers.toList()
val trackers2 = frames2.trackers.toList()
// Load current values for adjustable configs
loadConfigValues()
val skeleton1 = HumanPoseManager(
trackers1
)
val skeleton2 = HumanPoseManager(
trackers2
)
// Load server configs into the skeleton
skeleton1.loadFromConfig(server.configManager)
skeleton2.loadFromConfig(server.configManager)
// Disable leg tweaks, this will mess with the resulting positions
skeleton1.setLegTweaksEnabled(false)
skeleton2.setLegTweaksEnabled(false)
val intermediateOffsets = EnumMap(offsets)
// If target height isn't specified, auto-detect
if (targetHeight < 0f) {
targetHeight = getTargetHeight(frames)
// Set the target heights either from config or calculate them
val targetHmdHeight = if (config.targetHmdHeight > 0f) {
config.targetHmdHeight
} else {
calcTargetHmdHeight(frames, config)
}
val trainingStep = AutoBoneTrainingStep(
targetHeight,
skeleton1,
skeleton2,
frames,
intermediateOffsets
val targetFullHeight = if (config.targetFullHeight > 0f) {
config.targetFullHeight
} else {
targetHmdHeight * BodyProportionError.eyeHeightToHeightRatio
}
// Set up the current state, making all required players and setting up the
// skeletons appropriately
val trainingStep = AutoBoneStep(
config = config,
targetHmdHeight = targetHmdHeight,
targetFullHeight = targetFullHeight,
frames = frames,
intermediateOffsets = EnumMap(offsets),
epochCallback = epochCallback,
serverConfig = server.configManager
)
val errorStats = StatsCalculator()
// Initialize the frame order randomizer with a repeatable seed
rand.setSeed(config.randSeed)
// Epoch loop, each epoch is one full iteration over the full dataset
for (epoch in (if (calcInitError) -1 else 0) until config.numEpochs) {
val adjustRate = decayFunc(config.initialAdjustRate, config.adjustRateDecay, epoch)
for (epoch in (if (config.calcInitError) -1 else 0) until config.numEpochs) {
// Set the current epoch to process
trainingStep.curEpoch = epoch
var randomFrameIndices: IntArray? = null
if (config.randomizeFrameOrder) {
randomFrameIndices = IntArray(frameCount)
var zeroPos = -1
for (i in 0 until frameCount) {
var index = rand.nextInt(frameCount)
if (i > 0) {
while (index == zeroPos || randomFrameIndices[index] > 0) {
index = rand.nextInt(frameCount)
}
} else {
zeroPos = index
}
randomFrameIndices[index] = i
}
}
// Iterate over the frames using a cursor and an offset for comparing
// frames a certain number of frames apart
var cursorOffset = config.minDataDistance
while (cursorOffset <= config.maxDataDistance &&
cursorOffset < frameCount
) {
var frameCursor = 0
while (frameCursor < frameCount - cursorOffset) {
val frameCursor2 = frameCursor + cursorOffset
applyConfig(skeleton1)
applyConfig(skeleton2)
if (config.randomizeFrameOrder && randomFrameIndices != null) {
trainingStep
.setCursors(
randomFrameIndices[frameCursor],
randomFrameIndices[frameCursor2]
)
} else {
trainingStep.setCursors(frameCursor, frameCursor2)
}
frames1.setCursors(trainingStep.cursor1)
frames2.setCursors(trainingStep.cursor2)
skeleton1.update()
skeleton2.update()
val totalLength = getLengthSum(offsets)
val curHeight = sumSelectConfigs(heightOffsets, offsets)
trainingStep.currentHeight = curHeight
val errorDeriv = getErrorDeriv(trainingStep)
val error = errorFunc(errorDeriv)
// In case of fire
if (java.lang.Float.isNaN(error) || java.lang.Float.isInfinite(error)) {
// Extinguish
LogManager
.warning(
"[AutoBone] Error value is invalid, resetting variables to recover"
)
// Reset adjustable config values
loadConfigValues()
// Reset error sum values
errorStats.reset()
frameCursor += config.cursorIncrement
// Continue on new data
continue
}
// Store the error count for logging purposes
errorStats.addValue(errorDeriv)
val adjustVal = error * adjustRate
// If there is no adjustment whatsoever, skip this
if (adjustVal == 0f) {
frameCursor += config.cursorIncrement
continue
}
val slideLeft = skeleton2
.getComputedTracker(TrackerRole.LEFT_FOOT).position
-skeleton1.getComputedTracker(TrackerRole.LEFT_FOOT).position
val slideRight = skeleton2
.getComputedTracker(TrackerRole.RIGHT_FOOT).position
-skeleton1.getComputedTracker(TrackerRole.RIGHT_FOOT).position
intermediateOffsets.putAll(offsets)
for (entry in offsets.entries) {
// Skip adjustment if the epoch is before starting (for
// logging only)
if (epoch < 0) {
break
}
val originalLength = entry.value
val isHeightVar = heightOffsets.contains(entry.key)
val leftDotProduct = getDotProductDiff(
skeleton1,
skeleton2,
entry.key,
false,
slideLeft
)
val rightDotProduct = getDotProductDiff(
skeleton1,
skeleton2,
entry.key,
true,
slideRight
)
val dotLength = (
originalLength
* ((leftDotProduct + rightDotProduct) / 2f)
)
// Scale by the ratio for smooth adjustment and more
// stable results
val curAdjustVal = adjustVal * -dotLength / totalLength
val newLength = originalLength + curAdjustVal
// No small or negative numbers!!! Bad algorithm!
if (newLength < 0.01f) {
continue
}
// Apply new offset length
intermediateOffsets[entry.key] = newLength
applyConfig(skeleton1, intermediateOffsets)
applyConfig(skeleton2, intermediateOffsets)
// Update the skeleton poses for the new offset length
skeleton1.update()
skeleton2.update()
val newHeight = if (isHeightVar) curHeight + curAdjustVal else curHeight
trainingStep.currentHeight = newHeight
val newErrorDeriv = getErrorDeriv(trainingStep)
if (newErrorDeriv < errorDeriv) {
entry.setValue(newLength)
}
// Reset the length to minimize bias in other variables,
// it's applied later
intermediateOffsets[entry.key] = originalLength
applyConfig(skeleton1, intermediateOffsets)
applyConfig(skeleton2, intermediateOffsets)
}
if (config.scaleEachStep) {
val stepHeight = sumSelectConfigs(heightOffsets, offsets)
if (stepHeight > 0f) {
val stepHeightDiff = targetHeight - stepHeight
for (entry in offsets.entries) {
// Only height variables
if (entry.key == BoneType.NECK ||
!heightOffsets.contains(entry.key)
) {
continue
}
val length = entry.value
// Multiply the diff by the length to height
// ratio
val adjVal = stepHeightDiff * (length / stepHeight)
// Scale the length to fit the target height
entry.setValue((length + adjVal / 2f).coerceAtLeast(0.01f))
}
}
}
frameCursor += config.cursorIncrement
}
cursorOffset++
}
// Calculate average error over the epoch
if (epoch <= 0 || epoch >= config.numEpochs - 1 || (epoch + 1) % config.printEveryNumEpochs == 0) {
LogManager
.info(
"[AutoBone] Epoch: ${epoch + 1}, Mean error: ${errorStats.mean} (SD ${errorStats.standardDeviation}), Adjust rate: $adjustRate"
)
}
applyConfig(legacyConfigs)
epochCallback?.accept(Epoch(epoch + 1, config.numEpochs, errorStats, legacyConfigs))
// Process the epoch
internalEpoch(trainingStep)
}
val finalHeight = sumSelectConfigs(heightOffsets, offsets)
LogManager
.info(
"[AutoBone] Target height: $targetHeight, New height: $finalHeight"
"[AutoBone] Target height: ${trainingStep.targetHmdHeight}, New height: $finalHeight"
)
return AutoBoneResults(finalHeight, targetHeight, errorStats, legacyConfigs)
return AutoBoneResults(
finalHeight,
trainingStep.targetHmdHeight,
trainingStep.errorStats,
legacyConfigs
)
}
private fun internalEpoch(trainingStep: AutoBoneStep) {
// Pull frequently used variables out of trainingStep to reduce call length
val config = trainingStep.config
val frameCount = trainingStep.maxFrameCount
val errorStats = trainingStep.errorStats
val epoch = trainingStep.curEpoch
// Set the current adjust rate based on the current epoch
trainingStep.curAdjustRate = decayFunc(config.initialAdjustRate, config.adjustRateDecay, epoch)
var randomFrameIndices: IntArray? = null
if (config.randomizeFrameOrder) {
randomFrameIndices = IntArray(frameCount)
var zeroPos = -1
for (i in 0 until frameCount) {
var index = rand.nextInt(frameCount)
if (i > 0) {
while (index == zeroPos || randomFrameIndices[index] > 0) {
index = rand.nextInt(frameCount)
}
} else {
zeroPos = index
}
randomFrameIndices[index] = i
}
}
// Iterate over the frames using a cursor and an offset for comparing
// frames a certain number of frames apart
var cursorOffset = config.minDataDistance
while (cursorOffset <= config.maxDataDistance &&
cursorOffset < frameCount
) {
var frameCursor = 0
while (frameCursor < frameCount - cursorOffset) {
val frameCursor2 = frameCursor + cursorOffset
// Apply the current adjusted config to both skeletons
applyConfig(trainingStep.skeleton1)
applyConfig(trainingStep.skeleton2)
// Then set the frame cursors and apply them to both skeletons
if (config.randomizeFrameOrder && randomFrameIndices != null) {
trainingStep
.setCursors(
randomFrameIndices[frameCursor],
randomFrameIndices[frameCursor2],
updatePlayerCursors = true
)
} else {
trainingStep.setCursors(
frameCursor,
frameCursor2,
updatePlayerCursors = true
)
}
// Process the iteration
internalIter(trainingStep)
// Move on to the next iteration
frameCursor += config.cursorIncrement
}
cursorOffset++
}
// Calculate average error over the epoch
if (epoch <= 0 || epoch >= config.numEpochs - 1 || (epoch + 1) % config.printEveryNumEpochs == 0) {
LogManager
.info(
"[AutoBone] Epoch: ${epoch + 1}, Mean error: ${errorStats.mean} (SD ${errorStats.standardDeviation}), Adjust rate: ${trainingStep.curAdjustRate}"
)
}
// Convert current adjusted config to the legacy config format for the epoch
// callback, then call it
applyConfig(legacyConfigs)
trainingStep.epochCallback?.accept(Epoch(epoch + 1, config.numEpochs, errorStats, legacyConfigs))
}
private fun internalIter(trainingStep: AutoBoneStep) {
// Pull frequently used variables out of trainingStep to reduce call length
val skeleton1 = trainingStep.skeleton1
val skeleton2 = trainingStep.skeleton2
val intermediateOffsets = trainingStep.intermediateOffsets
val totalLength = getLengthSum(offsets)
val curHeight = sumSelectConfigs(heightOffsets, offsets)
trainingStep.currentHmdHeight = curHeight
val errorDeriv = getErrorDeriv(trainingStep)
val error = errorFunc(errorDeriv)
// In case of fire
if (java.lang.Float.isNaN(error) || java.lang.Float.isInfinite(error)) {
// Extinguish
LogManager
.warning(
"[AutoBone] Error value is invalid, resetting variables to recover"
)
// Reset adjustable config values
loadConfigValues()
// Reset error sum values
trainingStep.errorStats.reset()
// Continue on new data
return
}
// Store the error count for logging purposes
trainingStep.errorStats.addValue(errorDeriv)
val adjustVal = error * trainingStep.curAdjustRate
// If there is no adjustment whatsoever, skip this
if (adjustVal == 0f) {
return
}
val slideLeft = skeleton2
.getComputedTracker(TrackerRole.LEFT_FOOT).position -
skeleton1.getComputedTracker(TrackerRole.LEFT_FOOT).position
val slideRight = skeleton2
.getComputedTracker(TrackerRole.RIGHT_FOOT).position -
skeleton1.getComputedTracker(TrackerRole.RIGHT_FOOT).position
// Load the current offsets into the working offsets holder
intermediateOffsets.putAll(offsets)
for (entry in offsets.entries) {
// Skip adjustment if the epoch is before starting (for
// logging only)
if (trainingStep.curEpoch < 0) {
break
}
val originalLength = entry.value
val leftDotProduct = getDotProductDiff(
skeleton1,
skeleton2,
entry.key,
false,
slideLeft
)
val rightDotProduct = getDotProductDiff(
skeleton1,
skeleton2,
entry.key,
true,
slideRight
)
// Calculate the total effect of the bone based on change in rotation
val dotLength = (
originalLength
* ((leftDotProduct + rightDotProduct) / 2f)
)
// Scale by the ratio for smooth adjustment and more
// stable results
val curAdjustVal = adjustVal * -dotLength / totalLength
val newLength = originalLength + curAdjustVal
// No small or negative numbers!!! Bad algorithm!
if (newLength < 0.01f) {
continue
}
// Apply new offset length
intermediateOffsets[entry.key] = newLength
applyConfig(skeleton1, intermediateOffsets)
applyConfig(skeleton2, intermediateOffsets)
// Update the skeleton poses for the new offset length
skeleton1.update()
skeleton2.update()
val newErrorDeriv = getErrorDeriv(trainingStep)
if (newErrorDeriv < errorDeriv) {
// Apply the adjusted length to the current adjusted offsets
entry.setValue(newLength)
}
// Reset the length to minimize bias in other variables,
// it's applied later
intermediateOffsets[entry.key] = originalLength
applyConfig(skeleton1, intermediateOffsets)
applyConfig(skeleton2, intermediateOffsets)
}
if (trainingStep.config.scaleEachStep) {
// Scale to the target height if requested by the config
scaleToTargetHeight(trainingStep)
}
}
private fun scaleToTargetHeight(trainingStep: AutoBoneStep) {
val stepHeight = sumSelectConfigs(heightOffsets, offsets)
if (stepHeight > 0f) {
val stepHeightDiff = trainingStep.targetHmdHeight - stepHeight
for (entry in offsets.entries) {
// Only height variables
if (entry.key == BoneType.NECK ||
!heightOffsets.contains(entry.key)
) {
continue
}
val length = entry.value
// Multiply the diff by the length to height
// ratio
val adjVal = stepHeightDiff * (length / stepHeight)
// Scale the length to fit the target height
entry.setValue(
(length + adjVal / 2f).coerceAtLeast(
0.01f
)
)
}
}
}
@Throws(AutoBoneException::class)
protected fun getErrorDeriv(trainingStep: AutoBoneTrainingStep): Float {
private fun getErrorDeriv(trainingStep: AutoBoneStep): Float {
val config = trainingStep.config
var sumError = 0f
if (config.slideErrorFactor > 0f) {
sumError += slideError.getStepError(trainingStep) * config.slideErrorFactor

View File

@@ -85,9 +85,7 @@ class AutoBoneHandler(private val server: VRServer) {
private fun processFrames(frames: PoseFrames): AutoBoneResults {
return autoBone
.processFrames(
frames,
autoBone.config.calcInitError,
autoBone.config.targetHeight
frames
) { epoch: Epoch? -> listeners.forEach(Consumer { listener: AutoBoneListener -> listener.onAutoBoneEpoch(epoch!!) }) }
}
@@ -120,8 +118,8 @@ class AutoBoneHandler(private val server: VRServer) {
announceProcessStatus(AutoBoneProcessType.RECORD, "Recording...")
// 1000 samples at 20 ms per sample is 20 seconds
val sampleCount = autoBone.config.sampleCount
val sampleRate = autoBone.config.sampleRateMs
val sampleCount = autoBone.globalConfig.sampleCount
val sampleRate = autoBone.globalConfig.sampleRateMs
val framesFuture = poseRecorder
.startFrameRecording(
sampleCount,
@@ -140,7 +138,7 @@ class AutoBoneHandler(private val server: VRServer) {
// Save a recurring recording for users to send as debug info
announceProcessStatus(AutoBoneProcessType.RECORD, "Saving recording...")
autoBone.saveRecording(frames, "LastABRecording.pfr")
if (autoBone.config.saveRecordings) {
if (autoBone.globalConfig.saveRecordings) {
announceProcessStatus(
AutoBoneProcessType.RECORD,
"Saving recording (from config option)..."
@@ -323,13 +321,15 @@ class AutoBoneHandler(private val server: VRServer) {
skeletonConfigManagerBuffer.setOffsets(autoBoneResults.configValues)
val neckLength = skeletonConfigManagerBuffer
.getOffset(SkeletonConfigOffsets.NECK)
val upperChestLength = skeletonConfigManagerBuffer
.getOffset(SkeletonConfigOffsets.UPPER_CHEST)
val chestLength = skeletonConfigManagerBuffer
.getOffset(SkeletonConfigOffsets.CHEST)
val waistLength = skeletonConfigManagerBuffer
.getOffset(SkeletonConfigOffsets.WAIST)
val hipLength = skeletonConfigManagerBuffer
.getOffset(SkeletonConfigOffsets.HIP)
val torsoLength = chestLength + waistLength + hipLength
val torsoLength = upperChestLength + chestLength + waistLength + hipLength
val hipWidth = skeletonConfigManagerBuffer
.getOffset(SkeletonConfigOffsets.HIPS_WIDTH)
val legLength = (
@@ -341,7 +341,7 @@ class AutoBoneHandler(private val server: VRServer) {
val lowerLegLength = skeletonConfigManagerBuffer
.getOffset(SkeletonConfigOffsets.LOWER_LEG)
val neckTorso = neckLength / torsoLength
val chestTorso = chestLength / torsoLength
val chestTorso = (upperChestLength + chestLength) / torsoLength
val torsoWaist = hipWidth / torsoLength
val legTorso = legLength / torsoLength
val legBody = legLength / (torsoLength + neckLength)

View File

@@ -0,0 +1,67 @@
package dev.slimevr.autobone
import dev.slimevr.config.AutoBoneConfig
import dev.slimevr.config.ConfigManager
import dev.slimevr.poseframeformat.PoseFrames
import dev.slimevr.poseframeformat.player.TrackerFramesPlayer
import dev.slimevr.tracking.processor.BoneType
import dev.slimevr.tracking.processor.HumanPoseManager
import java.util.*
import java.util.function.Consumer
class AutoBoneStep(
val config: AutoBoneConfig,
val targetHmdHeight: Float,
val targetFullHeight: Float,
val frames: PoseFrames,
val intermediateOffsets: EnumMap<BoneType, Float>,
val epochCallback: Consumer<AutoBone.Epoch?>?,
serverConfig: ConfigManager,
var curEpoch: Int = 0,
var curAdjustRate: Float = 0f,
var cursor1: Int = 0,
var cursor2: Int = 0,
var currentHmdHeight: Float = 0f,
) {
val maxFrameCount = frames.maxFrameCount
val framePlayer1 = TrackerFramesPlayer(frames)
val framePlayer2 = TrackerFramesPlayer(frames)
val trackers1 = framePlayer1.trackers.toList()
val trackers2 = framePlayer2.trackers.toList()
val skeleton1 = HumanPoseManager(trackers1)
val skeleton2 = HumanPoseManager(trackers2)
val errorStats = StatsCalculator()
init {
// Load server configs into the skeleton
skeleton1.loadFromConfig(serverConfig)
skeleton2.loadFromConfig(serverConfig)
// Disable leg tweaks, this will mess with the resulting positions
skeleton1.setLegTweaksEnabled(false)
skeleton2.setLegTweaksEnabled(false)
}
fun setCursors(cursor1: Int, cursor2: Int, updatePlayerCursors: Boolean) {
this.cursor1 = cursor1
this.cursor2 = cursor2
if (updatePlayerCursors) {
updatePlayerCursors()
}
}
fun updatePlayerCursors() {
framePlayer1.setCursors(cursor1)
framePlayer2.setCursors(cursor2)
skeleton1.update()
skeleton2.update()
}
val heightOffset: Float
get() = targetHmdHeight - currentHmdHeight
}

View File

@@ -1,25 +0,0 @@
package dev.slimevr.autobone
import dev.slimevr.poseframeformat.PoseFrames
import dev.slimevr.tracking.processor.BoneType
import dev.slimevr.tracking.processor.HumanPoseManager
class AutoBoneTrainingStep(
val targetHeight: Float,
val humanPoseManager1: HumanPoseManager,
val humanPoseManager2: HumanPoseManager,
val trainingFrames: PoseFrames,
val intermediateOffsets: Map<BoneType, Float>,
var cursor1: Int = 0,
var cursor2: Int = 0,
var currentHeight: Float = 0f,
) {
fun setCursors(cursor1: Int, cursor2: Int) {
this.cursor1 = cursor1
this.cursor2 = cursor2
}
val heightOffset: Float
get() = targetHeight - currentHeight
}

View File

@@ -1,7 +1,7 @@
package dev.slimevr.autobone.errors
import com.jme3.math.FastMath
import dev.slimevr.autobone.AutoBoneTrainingStep
import dev.slimevr.autobone.AutoBoneStep
import dev.slimevr.autobone.errors.proportions.ProportionLimiter
import dev.slimevr.autobone.errors.proportions.RangeProportionLimiter
import dev.slimevr.tracking.processor.HumanPoseManager
@@ -10,10 +10,10 @@ import dev.slimevr.tracking.processor.config.SkeletonConfigOffsets
// The distance from average human proportions
class BodyProportionError : IAutoBoneError {
@Throws(AutoBoneException::class)
override fun getStepError(trainingStep: AutoBoneTrainingStep): Float {
override fun getStepError(trainingStep: AutoBoneStep): Float {
return getBodyProportionError(
trainingStep.humanPoseManager1,
trainingStep.currentHeight
trainingStep.skeleton1,
trainingStep.currentHmdHeight
)
}
@@ -39,7 +39,8 @@ class BodyProportionError : IAutoBoneError {
// Full Height: 1.58 / 0.936 = 1.688034
// Neck: 0.1 / 1.688034 = 0.059241
// Torso: 0.56 / 1.688034 = 0.331747
// Chest: 0.32 / 1.688034 = 0.18957
// Upper Chest: 0.16 / 1.688034 = 0.094784
// Chest: 0.16 / 1.688034 = 0.094784
// Waist: (0.56 - 0.32 - 0.04) / 1.688034 = 0.118481
// Hip: 0.04 / 1.688034 = 0.023696
// Hip Width: 0.26 / 1.688034 = 0.154025
@@ -63,12 +64,19 @@ class BodyProportionError : IAutoBoneError {
{ config: HumanPoseManager -> config.getOffset(SkeletonConfigOffsets.NECK) },
0.0015f
),
// Chest
// Experimental: 0.189
// Upper Chest
// Experimental: 0.0945
RangeProportionLimiter(
0.189f,
0.0945f,
{ config: HumanPoseManager -> config.getOffset(SkeletonConfigOffsets.UPPER_CHEST) },
0.01f
),
// Chest
// Experimental: 0.0945
RangeProportionLimiter(
0.0945f,
{ config: HumanPoseManager -> config.getOffset(SkeletonConfigOffsets.CHEST) },
0.02f
0.01f
),
// Waist
// Experimental: 0.118
@@ -114,12 +122,13 @@ class BodyProportionError : IAutoBoneError {
return when (offset) {
SkeletonConfigOffsets.HEAD -> proportionLimits[0]
SkeletonConfigOffsets.NECK -> proportionLimits[1]
SkeletonConfigOffsets.CHEST -> proportionLimits[2]
SkeletonConfigOffsets.WAIST -> proportionLimits[3]
SkeletonConfigOffsets.HIP -> proportionLimits[4]
SkeletonConfigOffsets.HIPS_WIDTH -> proportionLimits[5]
SkeletonConfigOffsets.UPPER_LEG -> proportionLimits[6]
SkeletonConfigOffsets.LOWER_LEG -> proportionLimits[7]
SkeletonConfigOffsets.UPPER_CHEST -> proportionLimits[2]
SkeletonConfigOffsets.CHEST -> proportionLimits[3]
SkeletonConfigOffsets.WAIST -> proportionLimits[4]
SkeletonConfigOffsets.HIP -> proportionLimits[5]
SkeletonConfigOffsets.HIPS_WIDTH -> proportionLimits[6]
SkeletonConfigOffsets.UPPER_LEG -> proportionLimits[7]
SkeletonConfigOffsets.LOWER_LEG -> proportionLimits[8]
else -> null
}
}

View File

@@ -1,7 +1,7 @@
package dev.slimevr.autobone.errors
import com.jme3.math.FastMath
import dev.slimevr.autobone.AutoBoneTrainingStep
import dev.slimevr.autobone.AutoBoneStep
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton
import dev.slimevr.tracking.trackers.Tracker
import dev.slimevr.tracking.trackers.TrackerRole
@@ -9,10 +9,10 @@ import dev.slimevr.tracking.trackers.TrackerRole
// The offset between the height both feet at one instant and over time
class FootHeightOffsetError : IAutoBoneError {
@Throws(AutoBoneException::class)
override fun getStepError(trainingStep: AutoBoneTrainingStep): Float {
override fun getStepError(trainingStep: AutoBoneStep): Float {
return getSlideError(
trainingStep.humanPoseManager1.skeleton,
trainingStep.humanPoseManager2.skeleton
trainingStep.skeleton1.skeleton,
trainingStep.skeleton2.skeleton
)
}

View File

@@ -1,15 +1,15 @@
package dev.slimevr.autobone.errors
import com.jme3.math.FastMath
import dev.slimevr.autobone.AutoBoneTrainingStep
import dev.slimevr.autobone.AutoBoneStep
// The difference from the current height to the target height
class HeightError : IAutoBoneError {
@Throws(AutoBoneException::class)
override fun getStepError(trainingStep: AutoBoneTrainingStep): Float {
override fun getStepError(trainingStep: AutoBoneStep): Float {
return getHeightError(
trainingStep.currentHeight,
trainingStep.targetHeight
trainingStep.currentHmdHeight,
trainingStep.targetHmdHeight
)
}

View File

@@ -1,8 +1,8 @@
package dev.slimevr.autobone.errors
import dev.slimevr.autobone.AutoBoneTrainingStep
import dev.slimevr.autobone.AutoBoneStep
interface IAutoBoneError {
@Throws(AutoBoneException::class)
fun getStepError(trainingStep: AutoBoneTrainingStep): Float
fun getStepError(trainingStep: AutoBoneStep): Float
}

View File

@@ -1,7 +1,7 @@
package dev.slimevr.autobone.errors
import com.jme3.math.FastMath
import dev.slimevr.autobone.AutoBoneTrainingStep
import dev.slimevr.autobone.AutoBoneStep
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton
import dev.slimevr.tracking.trackers.Tracker
import dev.slimevr.tracking.trackers.TrackerRole
@@ -9,10 +9,10 @@ import dev.slimevr.tracking.trackers.TrackerRole
// The change in distance between both of the ankles over time
class OffsetSlideError : IAutoBoneError {
@Throws(AutoBoneException::class)
override fun getStepError(trainingStep: AutoBoneTrainingStep): Float {
override fun getStepError(trainingStep: AutoBoneStep): Float {
return getSlideError(
trainingStep.humanPoseManager1.skeleton,
trainingStep.humanPoseManager2.skeleton
trainingStep.skeleton1.skeleton,
trainingStep.skeleton2.skeleton
)
}

View File

@@ -1,25 +1,25 @@
package dev.slimevr.autobone.errors
import dev.slimevr.autobone.AutoBoneTrainingStep
import dev.slimevr.autobone.AutoBoneStep
import dev.slimevr.poseframeformat.trackerdata.TrackerFrames
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton
// The distance of any points to the corresponding absolute position
class PositionError : IAutoBoneError {
@Throws(AutoBoneException::class)
override fun getStepError(trainingStep: AutoBoneTrainingStep): Float {
val trackers = trainingStep.trainingFrames.frameHolders
override fun getStepError(trainingStep: AutoBoneStep): Float {
val trackers = trainingStep.frames.frameHolders
return (
(
getPositionError(
trackers,
trainingStep.cursor1,
trainingStep.humanPoseManager1.skeleton
trainingStep.skeleton1.skeleton
) +
getPositionError(
trackers,
trainingStep.cursor2,
trainingStep.humanPoseManager2.skeleton
trainingStep.skeleton2.skeleton
)
) /
2f

View File

@@ -1,21 +1,21 @@
package dev.slimevr.autobone.errors
import com.jme3.math.FastMath
import dev.slimevr.autobone.AutoBoneTrainingStep
import dev.slimevr.autobone.AutoBoneStep
import dev.slimevr.poseframeformat.trackerdata.TrackerFrames
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton
// The difference between offset of absolute position and the corresponding point over time
class PositionOffsetError : IAutoBoneError {
@Throws(AutoBoneException::class)
override fun getStepError(trainingStep: AutoBoneTrainingStep): Float {
val trackers = trainingStep.trainingFrames.frameHolders
override fun getStepError(trainingStep: AutoBoneStep): Float {
val trackers = trainingStep.frames.frameHolders
return getPositionOffsetError(
trackers,
trainingStep.cursor1,
trainingStep.cursor2,
trainingStep.humanPoseManager1.skeleton,
trainingStep.humanPoseManager2.skeleton
trainingStep.skeleton1.skeleton,
trainingStep.skeleton2.skeleton
)
}

View File

@@ -1,6 +1,6 @@
package dev.slimevr.autobone.errors
import dev.slimevr.autobone.AutoBoneTrainingStep
import dev.slimevr.autobone.AutoBoneStep
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton
import dev.slimevr.tracking.trackers.Tracker
import dev.slimevr.tracking.trackers.TrackerRole
@@ -8,10 +8,10 @@ import dev.slimevr.tracking.trackers.TrackerRole
// The change in position of the ankle over time
class SlideError : IAutoBoneError {
@Throws(AutoBoneException::class)
override fun getStepError(trainingStep: AutoBoneTrainingStep): Float {
override fun getStepError(trainingStep: AutoBoneStep): Float {
return getSlideError(
trainingStep.humanPoseManager1.skeleton,
trainingStep.humanPoseManager2.skeleton
trainingStep.skeleton1.skeleton,
trainingStep.skeleton2.skeleton
)
}

View File

@@ -16,11 +16,13 @@ class AutoBoneConfig {
var positionErrorFactor = 0.0f
var positionOffsetErrorFactor = 0.0f
var calcInitError = false
var targetHeight = -1f
var targetHmdHeight = -1f
var targetFullHeight = -1f
var randomizeFrameOrder = true
var scaleEachStep = true
var sampleCount = 1000
var sampleRateMs: Long = 20
var saveRecordings = false
var useSkeletonHeight = false
var randSeed = 4L
}

View File

@@ -224,6 +224,25 @@ public class CurrentVRConfigConverter implements VersionedModelConverter {
tapDetectionNode.set("fullResetTaps", tapDetectionNode.get("resetTaps"));
}
}
if (version < 9) {
// split chest into 2 offsets
ObjectNode skeletonNode = (ObjectNode) modelData.get("skeleton");
if (skeletonNode != null) {
ObjectNode offsetsNode = (ObjectNode) skeletonNode.get("offsets");
if (offsetsNode != null) {
JsonNode chestNode = offsetsNode.get("chestLength");
if (chestNode != null) {
offsetsNode
.set("chestLength", new FloatNode(chestNode.floatValue() / 2f));
offsetsNode
.set(
"upperChestLength",
new FloatNode(chestNode.floatValue() / 2f)
);
}
}
}
}
} catch (Exception e) {
LogManager.severe("Error during config migration: " + e);
}

View File

@@ -14,7 +14,7 @@ import java.util.Map;
@JsonVersionedModel(
currentVersion = "8", defaultDeserializeToVersion = "8", toCurrentConverterClass = CurrentVRConfigConverter.class
currentVersion = "9", defaultDeserializeToVersion = "9", toCurrentConverterClass = CurrentVRConfigConverter.class
)
public class VRConfig {

View File

@@ -74,7 +74,7 @@ class UnityArmature(localRot: Boolean) {
}
fun updateNodes() {
// Set the upper chest's rotation to the chest's
// Set the upper chest node's rotation to the chest's
upperChestNode.localTransform.rotation = chestNode.localTransform.rotation
// Update the root node
hipNode.update()

View File

@@ -11,7 +11,6 @@ import dev.slimevr.platform.SteamVRBridge;
import dev.slimevr.tracking.processor.HumanPoseManager;
import dev.slimevr.tracking.trackers.Tracker;
import dev.slimevr.tracking.trackers.TrackerPosition;
import dev.slimevr.tracking.trackers.TrackerRole;
import dev.slimevr.tracking.trackers.TrackerStatus;
import io.eiren.util.collections.FastList;
import io.eiren.util.logging.LogManager;
@@ -298,7 +297,7 @@ public class VRCOSCHandler implements OSCHandler {
public void yawAlign() {
if (oscSender != null && oscSender.isConnected()) {
for (Tracker shareableTracker : computedTrackers) {
if (shareableTracker.getTrackerPosition().getTrackerRole() == TrackerRole.HEAD) {
if (shareableTracker.getTrackerPosition() == TrackerPosition.HEAD) {
var hmdAngles = shareableTracker.getRotation().toEulerAngles(EulerOrder.XYZ);
oscArgs.clear();
oscArgs.add(0f);

View File

@@ -4,13 +4,10 @@ import dev.slimevr.Main;
import dev.slimevr.VRServer;
import dev.slimevr.bridge.ProtobufBridge;
import dev.slimevr.bridge.ProtobufMessages;
import dev.slimevr.bridge.ProtobufMessages.*;
import dev.slimevr.bridge.ProtobufMessages.Battery;
import dev.slimevr.bridge.ProtobufMessages.ProtobufMessage;
import dev.slimevr.config.BridgeConfig;
import dev.slimevr.tracking.trackers.Device;
import dev.slimevr.tracking.trackers.Tracker;
import dev.slimevr.tracking.trackers.TrackerPosition;
import dev.slimevr.tracking.trackers.TrackerUtils;
import dev.slimevr.tracking.trackers.TrackerRole;
import dev.slimevr.tracking.trackers.*;
import dev.slimevr.util.ann.VRServerThread;
import solarxr_protocol.rpc.StatusData;
import solarxr_protocol.rpc.StatusDataUnion;
@@ -181,7 +178,7 @@ public abstract class SteamVRBridge extends ProtobufBridge implements Runnable {
tertiaryTracker = TrackerUtils
.getNonInternalTrackerForBodyPosition(
allTrackers,
TrackerPosition.CHEST
TrackerPosition.UPPER_CHEST
);
}
break;

View File

@@ -59,7 +59,7 @@ class PlayerTracker(val trackerFrames: TrackerFrames, val tracker: Tracker, priv
val acceleration = frame.tryGetAcceleration()
if (acceleration != null) {
tracker.acceleration = acceleration
tracker.setAcceleration(acceleration)
}
}
}

View File

@@ -117,7 +117,7 @@ data class TrackerFrame(
val rotation: Quaternion? = if (tracker.hasRotation) tracker.getRotation() else null
val position: Vector3? = if (tracker.hasPosition) tracker.position else null
val acceleration: Vector3? = if (tracker.hasAcceleration) tracker.acceleration else null
val acceleration: Vector3? = if (tracker.hasAcceleration) tracker.getAcceleration() else null
var rawRotation: Quaternion? = if (tracker.hasAdjustedRotation) tracker.getRawRotation() else null
// If the rawRotation is the same as rotation, there's no point in saving it, set it back to null

View File

@@ -164,6 +164,8 @@ public class ProvisioningHandler implements SerialListener {
@Override
public void onNewSerialDevice(SerialPort port) {
if (!isRunning)
return;
this.initSerial(this.preferredPort);
}

View File

@@ -11,8 +11,9 @@ public enum BoneType {
HEAD(BodyPart.HEAD),
HEAD_TRACKER(),
NECK(BodyPart.NECK),
CHEST(BodyPart.CHEST),
UPPER_CHEST(BodyPart.UPPER_CHEST),
CHEST_TRACKER,
CHEST(BodyPart.CHEST),
WAIST(BodyPart.WAIST),
HIP(BodyPart.HIP),
HIP_TRACKER,

View File

@@ -144,7 +144,7 @@ public class HumanPoseManager {
VRServer.getNextLocalTrackerId(),
"human://CHEST",
"Computed chest",
TrackerPosition.CHEST,
TrackerPosition.UPPER_CHEST,
null,
true,
true,

View File

@@ -125,6 +125,7 @@ public class SkeletonConfigManager {
private float calculateUserHeight() {
return getOffset(SkeletonConfigOffsets.NECK)
+ getOffset(SkeletonConfigOffsets.UPPER_CHEST)
+ getOffset(SkeletonConfigOffsets.CHEST)
+ getOffset(SkeletonConfigOffsets.WAIST)
+ getOffset(SkeletonConfigOffsets.HIP)
@@ -208,14 +209,21 @@ public class SkeletonConfigManager {
switch (nodeOffset) {
case HEAD -> setNodeOffset(nodeOffset, 0, 0, getOffset(SkeletonConfigOffsets.HEAD));
case NECK -> setNodeOffset(nodeOffset, 0, -getOffset(SkeletonConfigOffsets.NECK), 0);
case CHEST -> setNodeOffset(nodeOffset, 0, -getOffset(SkeletonConfigOffsets.CHEST), 0);
case UPPER_CHEST -> setNodeOffset(
nodeOffset,
0,
-getOffset(SkeletonConfigOffsets.UPPER_CHEST),
0
);
case CHEST_TRACKER -> setNodeOffset(
nodeOffset,
0,
-getOffset(SkeletonConfigOffsets.CHEST_OFFSET)
- getOffset(SkeletonConfigOffsets.UPPER_CHEST)
- getOffset(SkeletonConfigOffsets.CHEST),
-getOffset(SkeletonConfigOffsets.SKELETON_OFFSET)
);
case CHEST -> setNodeOffset(nodeOffset, 0, -getOffset(SkeletonConfigOffsets.CHEST), 0);
case WAIST -> setNodeOffset(nodeOffset, 0, -getOffset(SkeletonConfigOffsets.WAIST), 0);
case HIP -> setNodeOffset(nodeOffset, 0, -getOffset(SkeletonConfigOffsets.HIP), 0);
case HIP_TRACKER -> setNodeOffset(
@@ -423,7 +431,7 @@ public class SkeletonConfigManager {
}
switch (config) {
case CHEST, WAIST, HIP, UPPER_LEG, LOWER_LEG -> {
case UPPER_CHEST, CHEST, WAIST, HIP, UPPER_LEG, LOWER_LEG -> {
float height = humanPoseManager.getHmdHeight()
/ BodyProportionError.eyeHeightToHeightRatio;
if (height > 0.5f) { // Reset only if floor level seems right,

View File

@@ -7,6 +7,7 @@ import java.util.Map;
public enum SkeletonConfigOffsets {
// Note: GUI uses this order to sort (not ID)
HEAD(
1,
"headShift",
@@ -19,10 +20,16 @@ public enum SkeletonConfigOffsets {
0.1f,
new BoneType[] { BoneType.NECK }
),
UPPER_CHEST(
21,
"upperChestLength",
0.16f,
new BoneType[] { BoneType.UPPER_CHEST, BoneType.CHEST_TRACKER }
),
CHEST(
3,
"chestLength",
0.32f,
0.16f,
new BoneType[] { BoneType.CHEST, BoneType.CHEST_TRACKER }
),
CHEST_OFFSET(
@@ -150,7 +157,7 @@ public enum SkeletonConfigOffsets {
float defaultValue,
BoneType[] affectedOffsets
) {
this.id = id;
this.id = id; // id of SkeletonBone in solarxr
this.configKey = configKey;
this.defaultValue = defaultValue;

View File

@@ -34,8 +34,9 @@ public class HumanSkeleton {
protected final TransformNode headNode = new TransformNode(BoneType.HEAD, false);
protected final TransformNode trackerHeadNode = new TransformNode(BoneType.HEAD_TRACKER, false);
protected final TransformNode neckNode = new TransformNode(BoneType.NECK, false);
protected final TransformNode chestNode = new TransformNode(BoneType.CHEST, false);
protected final TransformNode upperChestNode = new TransformNode(BoneType.CHEST, false);
protected final TransformNode trackerChestNode = new TransformNode(BoneType.CHEST_TRACKER, false);
protected final TransformNode chestNode = new TransformNode(BoneType.CHEST, false);
protected final TransformNode waistNode = new TransformNode(BoneType.WAIST, false);
protected final TransformNode hipNode = new TransformNode(BoneType.HIP, false);
protected final TransformNode trackerHipNode = new TransformNode(BoneType.HIP_TRACKER, false);
@@ -89,6 +90,7 @@ public class HumanSkeleton {
// #region Tracker Input
protected Tracker headTracker;
protected Tracker neckTracker;
protected Tracker upperChestTracker;
protected Tracker chestTracker;
protected Tracker waistTracker;
protected Tracker hipTracker;
@@ -201,7 +203,8 @@ public class HumanSkeleton {
// #region Assemble skeleton from head to hip
hmdNode.attachChild(headNode);
headNode.attachChild(neckNode);
neckNode.attachChild(chestNode);
neckNode.attachChild(upperChestNode);
upperChestNode.attachChild(chestNode);
chestNode.attachChild(waistNode);
waistNode.attachChild(hipNode);
// #endregion
@@ -386,6 +389,11 @@ public class HumanSkeleton {
trackers,
TrackerPosition.NECK
);
upperChestTracker = TrackerUtils
.getNonInternalTrackerForBodyPosition(
trackers,
TrackerPosition.UPPER_CHEST
);
chestTracker = TrackerUtils
.getNonInternalTrackerForBodyPosition(
trackers,
@@ -473,7 +481,10 @@ public class HumanSkeleton {
);
// Check for specific conditions and store them in booleans.
hasSpineTracker = chestTracker != null || waistTracker != null || hipTracker != null;
hasSpineTracker = upperChestTracker != null
|| chestTracker != null
|| waistTracker != null
|| hipTracker != null;
hasKneeTrackers = leftUpperLegTracker != null && rightUpperLegTracker != null;
hasLeftLegTracker = leftUpperLegTracker != null
|| leftLowerLegTracker != null
@@ -497,7 +508,7 @@ public class HumanSkeleton {
protected void setComputedTracker(Tracker tracker) {
switch (tracker.getTrackerPosition()) {
case HEAD -> computedHeadTracker = tracker;
case CHEST -> computedChestTracker = tracker;
case UPPER_CHEST -> computedChestTracker = tracker;
case HIP -> computedHipTracker = tracker;
case LEFT_UPPER_LEG -> computedLeftKneeTracker = tracker;
case LEFT_FOOT -> computedLeftFootTracker = tracker;
@@ -591,7 +602,12 @@ public class HumanSkeleton {
headRot = neckTracker.getRotation();
} else if (hasSpineTracker) {
headRot = TrackerUtils
.getFirstAvailableTracker(chestTracker, waistTracker, hipTracker)
.getFirstAvailableTracker(
upperChestTracker,
chestTracker,
waistTracker,
hipTracker
)
.getRotation();
}
@@ -607,19 +623,28 @@ public class HumanSkeleton {
// Spine
if (hasSpineTracker) {
// Upper chest
Quaternion torsoRot = TrackerUtils
.getFirstAvailableTracker(chestTracker, waistTracker, hipTracker)
.getFirstAvailableTracker(upperChestTracker, chestTracker, waistTracker, hipTracker)
.getRotation();
neckNode.getLocalTransform().setRotation(torsoRot);
trackerChestNode.getLocalTransform().setRotation(torsoRot);
// Chest
torsoRot = TrackerUtils
.getFirstAvailableTracker(waistTracker, chestTracker, hipTracker)
.getFirstAvailableTracker(chestTracker, upperChestTracker, waistTracker, hipTracker)
.getRotation();
upperChestNode.getLocalTransform().setRotation(torsoRot);
// Waist
torsoRot = TrackerUtils
.getFirstAvailableTracker(waistTracker, chestTracker, upperChestTracker, hipTracker)
.getRotation();
chestNode.getLocalTransform().setRotation(torsoRot);
// Hip
torsoRot = TrackerUtils
.getFirstAvailableTracker(hipTracker, waistTracker, chestTracker)
.getFirstAvailableTracker(hipTracker, waistTracker, chestTracker, upperChestTracker)
.getRotation();
waistNode.getLocalTransform().setRotation(torsoRot);
hipNode.getLocalTransform().setRotation(torsoRot);
@@ -630,6 +655,7 @@ public class HumanSkeleton {
neckNode.getLocalTransform().setRotation(yawQuat);
trackerChestNode.getLocalTransform().setRotation(yawQuat);
upperChestNode.getLocalTransform().setRotation(yawQuat);
chestNode.getLocalTransform().setRotation(yawQuat);
waistNode.getLocalTransform().setRotation(yawQuat);
hipNode.getLocalTransform().setRotation(yawQuat);
@@ -730,10 +756,12 @@ public class HumanSkeleton {
// Tries to guess missing lower spine trackers by interpolating
// rotations
if (waistTracker == null) {
if (chestTracker != null && hipTracker != null) {
if ((upperChestTracker != null || chestTracker != null) && hipTracker != null) {
// Calculates waist from chest + hip
var hipRot = hipTracker.getRotation();
var chestRot = chestTracker.getRotation();
var chestRot = TrackerUtils
.getFirstAvailableTracker(upperChestTracker, chestTracker)
.getRotation();
// Get the rotation relative to where we expect the hip to
// be
@@ -745,11 +773,13 @@ public class HumanSkeleton {
chestRot = chestRot.interpQ(hipRot, waistFromChestHipAveraging);
chestNode.getLocalTransform().setRotation(chestRot);
} else if (chestTracker != null && hasKneeTrackers) {
} else if ((upperChestTracker != null || chestTracker != null) && hasKneeTrackers) {
// Calculates waist from chest + legs
var leftHipRot = leftHipNode.getLocalTransform().getRotation();
var rightHipRot = rightHipNode.getLocalTransform().getRotation();
var chestRot = chestTracker.getRotation();
var chestRot = TrackerUtils
.getFirstAvailableTracker(upperChestTracker, chestTracker)
.getRotation();
// Get the rotation relative to where we expect the
// upper legs to be
@@ -796,11 +826,13 @@ public class HumanSkeleton {
waistNode.getLocalTransform().setRotation(waistRot);
hipNode.getLocalTransform().setRotation(waistRot);
trackerHipNode.getLocalTransform().setRotation(waistRot);
} else if (chestTracker != null) {
} else if (upperChestTracker != null || chestTracker != null) {
// Calculates hip from chest + legs
var leftHipRot = leftHipNode.getLocalTransform().getRotation();
var rightHipRot = rightHipNode.getLocalTransform().getRotation();
var chestRot = chestTracker.getRotation();
var chestRot = TrackerUtils
.getFirstAvailableTracker(upperChestTracker, chestTracker)
.getRotation();
// Get the rotation relative to where we expect the
// upper legs to be
@@ -1118,8 +1150,9 @@ public class HumanSkeleton {
}
}
case NECK -> neckNode.getLocalTransform().setTranslation(offset);
case CHEST -> chestNode.getLocalTransform().setTranslation(offset);
case UPPER_CHEST -> upperChestNode.getLocalTransform().setTranslation(offset);
case CHEST_TRACKER -> trackerChestNode.getLocalTransform().setTranslation(offset);
case CHEST -> chestNode.getLocalTransform().setTranslation(offset);
case WAIST -> waistNode.getLocalTransform().setTranslation(offset);
case HIP -> hipNode.getLocalTransform().setTranslation(offset);
case HIP_TRACKER -> trackerHipNode.getLocalTransform().setTranslation(offset);
@@ -1216,8 +1249,9 @@ public class HumanSkeleton {
case HMD, HEAD -> headNode;
case HEAD_TRACKER -> trackerHeadNode;
case NECK -> neckNode;
case CHEST -> chestNode;
case UPPER_CHEST -> upperChestNode;
case CHEST_TRACKER -> trackerChestNode;
case CHEST -> chestNode;
case WAIST -> waistNode;
case HIP -> hipNode;
case HIP_TRACKER -> trackerHipNode;
@@ -1276,8 +1310,9 @@ public class HumanSkeleton {
headNode,
trackerHeadNode,
neckNode,
chestNode,
upperChestNode,
trackerChestNode,
chestNode,
waistNode,
hipNode,
trackerHipNode,

View File

@@ -83,10 +83,9 @@ public class LegTweakBuffer {
// hyperparameters
public static final float SKATING_DISTANCE_CUTOFF = 0.5f;
static float SKATING_VELOCITY_THRESHOLD = 2.4f;
static float SKATING_ACCELERATION_THRESHOLD = 0.7f;
static float SKATING_ACCELERATION_THRESHOLD = 0.8f;
private static final float SKATING_ROTVELOCITY_THRESHOLD = 4.5f;
private static final float SKATING_LOCK_ENGAGE_PERCENT = 0.85f;
private static final float SKATING_ACCELERATION_Y_USE_PERCENT = 0.25f;
private static final float FLOOR_DISTANCE_CUTOFF = 0.125f;
private static final float SIX_TRACKER_TOLERANCE = -0.10f;
private static final Vector3 FORCE_VECTOR_TO_PRESSURE = new Vector3(0.25f, 1.0f, 0.25f);
@@ -523,17 +522,9 @@ public class LegTweakBuffer {
// compute the acceleration magnitude of the feet from the acceleration
// given by the imus (exclude y)
private void computeAccelerationMagnitude() {
leftFootAccelerationMagnitude = new Vector3(
leftFootAcceleration.getX(),
leftFootAcceleration.getY() * SKATING_ACCELERATION_Y_USE_PERCENT,
leftFootAcceleration.getZ()
).len();
leftFootAccelerationMagnitude = leftFootAcceleration.len();
rightFootAccelerationMagnitude = new Vector3(
rightFootAcceleration.getX(),
rightFootAcceleration.getY() * SKATING_ACCELERATION_Y_USE_PERCENT,
rightFootAcceleration.getZ()
).len();
rightFootAccelerationMagnitude = rightFootAcceleration.len();
}
// compute the velocity and acceleration of the center of mass

View File

@@ -47,13 +47,14 @@ public class LegTweaks {
// hyperparameters (COM calculation)
// mass percentages of the body
private static final float HEAD_MASS = 0.082f;
private static final float CHEST_MASS = 0.25f;
private static final float WAIST_MASS = 0.209f;
private static final float THIGH_MASS = 0.128f;
private static final float CALF_MASS = 0.0535f;
private static final float UPPER_ARM_MASS = 0.031f;
private static final float FOREARM_MASS = 0.017f;
private static final float HEAD_MASS = 0.0827f;
private static final float THORAX_MASS = 0.1870f;
private static final float ABDOMEN_MASS = 0.1320f;
private static final float PELVIS_MASS = 0.1530f;
private static final float THIGH_MASS = 0.1122f;
private static final float LEG_AND_FOOT_MASS = 0.0620f;
private static final float UPPER_ARM_MASS = 0.0263f;
private static final float FOREARM_AND_HAND_MASS = 0.0224f;
// hyperparameters (rotation correction)
private static final float ROTATION_CORRECTION_VERTICAL = 0.1f;
@@ -1096,17 +1097,19 @@ public class LegTweaks {
// compute the center of mass of smaller body parts and then sum them up
// with their respective weights
Vector3 head = skeleton.headNode.getWorldTransform().getTranslation();
Vector3 chest = skeleton.chestNode.getWorldTransform().getTranslation();
Vector3 hip = skeleton.hipNode.getWorldTransform().getTranslation();
Vector3 thorax = getCenterOfJoint(skeleton.chestNode, skeleton.upperChestNode);
Vector3 abdomen = skeleton.waistNode.getWorldTransform().getTranslation();
Vector3 pelvis = skeleton.hipNode.getWorldTransform().getTranslation();
Vector3 leftCalf = getCenterOfJoint(skeleton.leftAnkleNode, skeleton.leftKneeNode);
Vector3 rightCalf = getCenterOfJoint(skeleton.rightAnkleNode, skeleton.rightKneeNode);
Vector3 leftThigh = getCenterOfJoint(skeleton.leftKneeNode, skeleton.leftHipNode);
Vector3 rightThigh = getCenterOfJoint(skeleton.rightKneeNode, skeleton.rightHipNode);
centerOfMass = centerOfMass.plus(head.times(HEAD_MASS));
centerOfMass = centerOfMass.plus(chest.times(CHEST_MASS));
centerOfMass = centerOfMass.plus(hip.times(WAIST_MASS));
centerOfMass = centerOfMass.plus(leftCalf.times(CALF_MASS));
centerOfMass = centerOfMass.plus(rightCalf.times(CALF_MASS));
centerOfMass = centerOfMass.plus(thorax.times(THORAX_MASS));
centerOfMass = centerOfMass.plus(abdomen.times(ABDOMEN_MASS));
centerOfMass = centerOfMass.plus(pelvis.times(PELVIS_MASS));
centerOfMass = centerOfMass.plus(leftCalf.times(LEG_AND_FOOT_MASS));
centerOfMass = centerOfMass.plus(rightCalf.times(LEG_AND_FOOT_MASS));
centerOfMass = centerOfMass.plus(leftThigh.times(THIGH_MASS));
centerOfMass = centerOfMass.plus(rightThigh.times(THIGH_MASS));
@@ -1126,17 +1129,17 @@ public class LegTweaks {
);
centerOfMass = centerOfMass.plus(leftUpperArm.times(UPPER_ARM_MASS));
centerOfMass = centerOfMass.plus(rightUpperArm.times(UPPER_ARM_MASS));
centerOfMass = centerOfMass.plus(leftForearm.times(FOREARM_MASS));
centerOfMass = centerOfMass.plus(rightForearm.times(FOREARM_MASS));
centerOfMass = centerOfMass.plus(leftForearm.times(FOREARM_AND_HAND_MASS));
centerOfMass = centerOfMass.plus(rightForearm.times(FOREARM_AND_HAND_MASS));
} else {
// if the arms are not available put them slightly in front
// of the chest.
// of the upper chest.
Vector3 chestUnitVector = computeUnitVector(
skeleton.chestNode.getWorldTransform().getRotation()
skeleton.upperChestNode.getWorldTransform().getRotation()
);
Vector3 armLocation = chest.plus(chestUnitVector.times(DEFAULT_ARM_DISTANCE));
Vector3 armLocation = abdomen.plus(chestUnitVector.times(DEFAULT_ARM_DISTANCE));
centerOfMass = centerOfMass.plus(armLocation.times(UPPER_ARM_MASS * 2.0f));
centerOfMass = centerOfMass.plus(armLocation.times(FOREARM_MASS * 2.0f));
centerOfMass = centerOfMass.plus(armLocation.times(FOREARM_AND_HAND_MASS * 2.0f));
}
// finally translate in to tracker space

View File

@@ -5,7 +5,7 @@ import dev.slimevr.tracking.trackers.Tracker;
import java.util.LinkedList;
// class that monitors the acceleration of the waist, hip, or chest trackers to detect taps
// class that monitors the acceleration of the waist, hip, chest or upper chest trackers to detect taps
// and use this to trigger a varaity of resets (if your wondering why no single tap class exists, it's because
// to many false positives)
public class TapDetection {
@@ -175,6 +175,13 @@ public class TapDetection {
// you need two or more trackers for this feature to be reliable)
private boolean isUserStatic(Tracker trackerToExclude) {
int num = 0;
if (
skeleton.upperChestTracker != null
&& !skeleton.upperChestTracker.equals(trackerToExclude)
) {
if (skeleton.upperChestTracker.getAcceleration().lenSq() > ALLOWED_BODY_ACCEL_SQUARED)
num++;
}
if (skeleton.chestTracker != null && !skeleton.chestTracker.equals(trackerToExclude)) {
if (skeleton.chestTracker.getAcceleration().lenSq() > ALLOWED_BODY_ACCEL_SQUARED)
num++;

View File

@@ -1,9 +1,6 @@
package dev.slimevr.tracking.processor.skeleton;
import java.util.ArrayList;
import java.util.List;
import dev.slimevr.config.TapDetectionConfig;
import dev.slimevr.reset.ResetHandler;
import dev.slimevr.setup.TapSetupHandler;
@@ -11,6 +8,9 @@ import dev.slimevr.tracking.processor.HumanPoseManager;
import dev.slimevr.tracking.trackers.Tracker;
import solarxr_protocol.rpc.ResetType;
import java.util.ArrayList;
import java.util.List;
/**
* Handles tap detection for reset
@@ -211,7 +211,9 @@ public class TapDetectionManager {
// on which one is available
// if none are available, returns null
private Tracker getTrackerToWatchYawReset() {
if (skeleton.chestTracker != null)
if (skeleton.upperChestTracker != null)
return skeleton.upperChestTracker;
else if (skeleton.chestTracker != null)
return skeleton.chestTracker;
else if (skeleton.hipTracker != null)
return skeleton.hipTracker;

View File

@@ -13,6 +13,7 @@ import solarxr_protocol.rpc.StatusData
import solarxr_protocol.rpc.StatusDataUnion
import solarxr_protocol.rpc.StatusTrackerErrorT
import solarxr_protocol.rpc.StatusTrackerResetT
import kotlin.properties.Delegates
const val TIMEOUT_MS = 2000L
@@ -45,8 +46,8 @@ class Tracker @JvmOverloads constructor(
private val timer = BufferedTimer(1f)
private var timeAtLastUpdate: Long = 0
private var rotation = Quaternion.IDENTITY
private var acceleration = Vector3.NULL
var position = Vector3.NULL
var acceleration = Vector3.NULL
val resetsHandler: TrackerResetsHandler = TrackerResetsHandler(this)
val filteringHandler: TrackerFilteringHandler = TrackerFilteringHandler()
var batteryVoltage: Float? = null
@@ -59,40 +60,37 @@ class Tracker @JvmOverloads constructor(
/**
* If the tracker has gotten disconnected after it was initialized first time
*/
var disconnectedRecently = false
var statusResetRecently = false
private var alreadyInitialized = false
var status = TrackerStatus.DISCONNECTED
set(value) {
if (field == value) return
var status: TrackerStatus by Delegates.observable(TrackerStatus.DISCONNECTED) {
_, old, new ->
if (old == new) return@observable
field = value
if (field == TrackerStatus.DISCONNECTED && alreadyInitialized) {
disconnectedRecently = true
}
if (field.sendData) {
alreadyInitialized = true
}
if (!isInternal) {
// If the status of a non-internal tracker has changed, inform
// the VRServer to recreate the skeleton, as it may need to
// assign or un-assign the tracker to a body part
vrServer.updateSkeletonModel()
checkReportErrorStatus()
checkReportRequireReset()
if (!new.reset) {
if (alreadyInitialized) {
statusResetRecently = true
}
alreadyInitialized = true
}
if (!isInternal) {
// If the status of a non-internal tracker has changed, inform
// the VRServer to recreate the skeleton, as it may need to
// assign or un-assign the tracker to a body part
vrServer.updateSkeletonModel()
var trackerPosition = trackerPosition
set(value) {
if (field == value) return
field = value
if (!isInternal) {
checkReportRequireReset()
}
checkReportErrorStatus()
checkReportRequireReset()
}
}
var trackerPosition: TrackerPosition? by Delegates.observable(trackerPosition) {
_, old, new ->
if (old == new) return@observable
if (!isInternal) {
checkReportRequireReset()
}
}
// Computed value to simplify availability checks
val hasAdjustedRotation = hasRotation && (allowFiltering || needsReset)
@@ -116,9 +114,11 @@ class Tracker @JvmOverloads constructor(
}
private fun checkReportRequireReset() {
if (needsReset && trackerPosition != null && lastResetStatus == 0u && status.sendData) {
if (needsReset && trackerPosition != null && lastResetStatus == 0u &&
!status.reset && (isImu() || !statusResetRecently)
) {
reportRequireReset()
} else if (lastResetStatus != 0u && (trackerPosition == null || !status.sendData)) {
} else if (lastResetStatus != 0u && (trackerPosition == null || status.reset)) {
vrServer.statusSystem.removeStatus(lastResetStatus)
lastResetStatus = 0u
}
@@ -203,6 +203,11 @@ class Tracker @JvmOverloads constructor(
resetsHandler.allowDriftCompensation = it
}
}
if (!isInternal &&
!(!isImu() && (trackerPosition == TrackerPosition.LEFT_HAND || trackerPosition == TrackerPosition.RIGHT_HAND))
) {
checkReportRequireReset()
}
}
/**
@@ -264,6 +269,18 @@ class Tracker @JvmOverloads constructor(
return rot
}
/**
* Gets the adjusted tracker acceleration after mounting corrections.
*/
fun getAcceleration(): Vector3 {
var vec = acceleration
if (needsReset) {
vec = resetsHandler.getMountingAdjustedRotationFrom(rotation).sandwich(vec)
}
return vec
}
/**
* Gets the identity-adjusted tracker rotation after some corrections
* (filtering, identity reset and identity mounting).
@@ -301,6 +318,13 @@ class Tracker @JvmOverloads constructor(
this.rotation = rotation
}
/**
* Sets the raw (unadjusted) acceleration of the tracker.
*/
fun setAcceleration(vec: Vector3) {
this.acceleration = vec
}
fun isImu(): Boolean {
return imuType != null
}

View File

@@ -15,7 +15,8 @@ enum class TrackerPosition(
) {
HEAD("body:head", TrackerRole.HMD, BodyPart.HEAD),
NECK("body:neck", TrackerRole.NECK, BodyPart.NECK),
CHEST("body:chest", TrackerRole.CHEST, BodyPart.CHEST),
UPPER_CHEST("body:upper_chest", TrackerRole.CHEST, BodyPart.UPPER_CHEST),
CHEST("body:chest", null, BodyPart.CHEST),
WAIST("body:waist", null, BodyPart.WAIST),
HIP("body:hip", TrackerRole.WAIST, BodyPart.HIP),
LEFT_UPPER_LEG("body:left_upper_leg", TrackerRole.LEFT_KNEE, BodyPart.LEFT_UPPER_LEG),

View File

@@ -96,6 +96,13 @@ class TrackerResetsHandler(val tracker: Tracker) {
return adjustToIdentity(rotation)
}
/**
* Takes a rotation and adjusts it to mounting
*/
fun getMountingAdjustedRotationFrom(rotation: Quaternion): Quaternion {
return rotation * mountingOrientation * mountRotFix
}
/**
* Converts raw or filtered rotation into reference- and
* mounting-reset-adjusted by applying quaternions produced after
@@ -185,9 +192,9 @@ class TrackerResetsHandler(val tracker: Tracker) {
// Let's just remove the status if you do yaw reset if the tracker was
// disconnected and then connected back
if (this.tracker.lastResetStatus != 0u && this.tracker.disconnectedRecently) {
if (this.tracker.lastResetStatus != 0u && this.tracker.statusResetRecently) {
vrServer.statusSystem.removeStatus(this.tracker.lastResetStatus)
this.tracker.disconnectedRecently = false
this.tracker.statusResetRecently = false
this.tracker.lastResetStatus = 0u
}
}

View File

@@ -1,12 +1,12 @@
package dev.slimevr.tracking.trackers
enum class TrackerStatus(val id: Int, val sendData: Boolean) {
enum class TrackerStatus(val id: Int, val sendData: Boolean, val reset: Boolean) {
DISCONNECTED(0, false),
OK(1, true),
BUSY(2, true),
ERROR(3, false),
OCCLUDED(4, false),
DISCONNECTED(0, false, true),
OK(1, true, false),
BUSY(2, true, false),
ERROR(3, false, true),
OCCLUDED(4, false, false),
;
companion object {

View File

@@ -43,4 +43,18 @@ object TrackerUtils {
secondTracker: Tracker?,
thirdTracker: Tracker?,
): Tracker? = firstTracker ?: (secondTracker ?: thirdTracker)
/**
* Returns the first tracker that isn't null out of the 4 trackers passed as
* arguments.
*
* @return The first non-null tracker or null
*/
@JvmStatic
fun getFirstAvailableTracker(
firstTracker: Tracker?,
secondTracker: Tracker?,
thirdTracker: Tracker?,
fourthTracker: Tracker?,
): Tracker? = firstTracker ?: (secondTracker ?: (thirdTracker ?: fourthTracker))
}

View File

@@ -293,8 +293,7 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
is UDPPacket4Acceleration -> {
tracker = connection?.getTracker(packet.sensorId)
if (tracker == null) return
val acceleration = tracker.getRotation().sandwich(packet.acceleration)
tracker.acceleration = acceleration
tracker.setAcceleration(packet.acceleration)
}
is UDPPacket10PingPong -> {
if (connection == null) return